From b67df83faf3174f74e2dfcb413edf8915f26f1f2 Mon Sep 17 00:00:00 2001 From: Tuncay Tunc Date: Tue, 21 Mar 2023 14:52:00 +0100 Subject: [PATCH 001/132] Generate OpenApi Spec --- .../control-plane-adapter/build.gradle.kts | 1 + .../edc/cp/adapter/HttpController.java | 2 + .../openapi/yaml/control-plane-adapter.yaml | 40 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 resources/openapi/yaml/control-plane-adapter.yaml diff --git a/edc-extensions/control-plane-adapter/build.gradle.kts b/edc-extensions/control-plane-adapter/build.gradle.kts index 715b5da74..a6ba7f28e 100644 --- a/edc-extensions/control-plane-adapter/build.gradle.kts +++ b/edc-extensions/control-plane-adapter/build.gradle.kts @@ -2,6 +2,7 @@ plugins { `java-library` `maven-publish` + id("io.swagger.core.v3.swagger-gradle-plugin") } dependencies { diff --git a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/HttpController.java b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/HttpController.java index 1d6bbc3fa..111d57068 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/HttpController.java +++ b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/HttpController.java @@ -16,6 +16,7 @@ import static java.util.Objects.isNull; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -35,6 +36,7 @@ @Produces({MediaType.APPLICATION_JSON}) @Path("/adapter/asset") @RequiredArgsConstructor +@Tag(name = "Control Plane Adapter") public class HttpController { private final Monitor monitor; private final ResultService resultService; diff --git a/resources/openapi/yaml/control-plane-adapter.yaml b/resources/openapi/yaml/control-plane-adapter.yaml new file mode 100644 index 000000000..c54839524 --- /dev/null +++ b/resources/openapi/yaml/control-plane-adapter.yaml @@ -0,0 +1,40 @@ +openapi: 3.0.1 +paths: + /adapter/asset/sync/{assetId}: + get: + operationId: getAssetSynchronous + parameters: + - in: path + name: assetId + required: true + schema: + type: string + example: null + - in: query + name: providerUrl + schema: + type: string + example: null + - in: query + name: contractAgreementId + schema: + type: string + example: null + - in: query + name: contractAgreementReuse + schema: + type: boolean + default: true + example: null + - in: query + name: timeout + schema: + type: string + example: null + responses: + default: + content: + application/json: {} + description: default response + tags: + - Control Plane Adapter From 5ea8fb48d48ded4206063b41e9aff63e51f82374 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Mon, 27 Mar 2023 16:07:25 +0200 Subject: [PATCH 002/132] feat(baseImage): replace alpine with temurin as base image for running java application --- .github/workflows/build.yaml | 8 ++++---- .github/workflows/business-tests.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 4 ++-- .github/workflows/veracode.yaml | 6 +++--- .github/workflows/verify.yaml | 12 ++++++------ .../src/main/docker/Dockerfile | 7 +------ .../src/main/docker/Dockerfile | 7 +------ .../src/main/docker/Dockerfile | 7 +------ .../src/main/docker/Dockerfile | 7 +------ .../src/main/docker/Dockerfile | 7 +------ .../src/main/docker/Dockerfile | 7 +------ 12 files changed, 23 insertions(+), 53 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 78b91b6f6..a3afa87e0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -77,7 +77,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' # Build - @@ -121,7 +121,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' # Build - @@ -188,7 +188,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' # Build - @@ -243,7 +243,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Import GPG Key uses: crazy-max/ghaction-import-gpg@v1 diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index f55d3d6ba..e2135cf07 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -56,7 +56,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Cache ContainerD Image Layers diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 9c4e888c8..955284359 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -37,7 +37,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Bump version in gradle.properties diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index d2064264f..88c5fe041 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -61,7 +61,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Import GPG Key @@ -181,7 +181,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Merge main back into develop and set new snapshot version diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index 722458663..0bfaac8b5 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -34,7 +34,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Verify proper formatting @@ -63,7 +63,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' # Build - @@ -112,7 +112,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' # Build - diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index adfeb5558..1ba38e785 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -69,7 +69,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Verify proper formatting @@ -91,7 +91,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Run Unit tests @@ -108,7 +108,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Run Integration tests @@ -125,7 +125,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Run API tests @@ -142,7 +142,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Run E2E tests @@ -165,7 +165,7 @@ jobs: uses: actions/setup-java@v3.10.0 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' cache: 'gradle' - name: Cache SonarCloud packages diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile index c7c6d2c81..229c44868 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile @@ -26,17 +26,12 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.17.1 - +FROM eclipse-temurin:11.0.18_10-jre-alpine ARG JAR ARG APP_USER=docker ARG APP_UID=10100 -RUN apk update && \ - apk add openjdk11-jre-headless=11.0.18_p10-r0 --no-cache && \ - rm -rf /var/cache/apk/* - RUN addgroup --system "$APP_USER" RUN adduser \ diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index 3f9a9806b..b3e04fac7 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -26,17 +26,12 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.17.2 - +FROM eclipse-temurin:11.0.18_10-jre-alpine ARG JAR ARG APP_USER=docker ARG APP_UID=10100 -RUN apk update && \ - apk add openjdk11-jre-headless=11.0.18_p10-r0 --no-cache && \ - rm -rf /var/cache/apk/* - RUN addgroup --system "$APP_USER" RUN adduser \ diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index 3f9a9806b..b3e04fac7 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -26,17 +26,12 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.17.2 - +FROM eclipse-temurin:11.0.18_10-jre-alpine ARG JAR ARG APP_USER=docker ARG APP_UID=10100 -RUN apk update && \ - apk add openjdk11-jre-headless=11.0.18_p10-r0 --no-cache && \ - rm -rf /var/cache/apk/* - RUN addgroup --system "$APP_USER" RUN adduser \ diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index 3f9a9806b..b3e04fac7 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -26,17 +26,12 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.17.2 - +FROM eclipse-temurin:11.0.18_10-jre-alpine ARG JAR ARG APP_USER=docker ARG APP_UID=10100 -RUN apk update && \ - apk add openjdk11-jre-headless=11.0.18_p10-r0 --no-cache && \ - rm -rf /var/cache/apk/* - RUN addgroup --system "$APP_USER" RUN adduser \ diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index 605a6d03c..5c3b12f11 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -26,17 +26,12 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.17.2 - +FROM eclipse-temurin:11.0.18_10-jre-alpine ARG JAR ARG APP_USER=docker ARG APP_UID=10100 -RUN apk update && \ - apk add openjdk11-jre-headless=11.0.18_p10-r0 --no-cache && \ - rm -rf /var/cache/apk/* - RUN addgroup --system "$APP_USER" RUN adduser \ diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 605a6d03c..5c3b12f11 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -26,17 +26,12 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM alpine:3.17.2 - +FROM eclipse-temurin:11.0.18_10-jre-alpine ARG JAR ARG APP_USER=docker ARG APP_UID=10100 -RUN apk update && \ - apk add openjdk11-jre-headless=11.0.18_p10-r0 --no-cache && \ - rm -rf /var/cache/apk/* - RUN addgroup --system "$APP_USER" RUN adduser \ From 84d58ee5ac51b98af749057801770bef04da596d Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Wed, 15 Mar 2023 17:02:41 +0100 Subject: [PATCH 003/132] Lint and refactor mostly all *.md files --- .github/ISSUE_TEMPLATE/bug_report.md | 13 +- .github/ISSUE_TEMPLATE/feature_request.md | 4 +- CHANGELOG.md | 112 +++++++--------- CODE_OF_CONDUCT.md | 22 +-- CONTRIBUTING.md | 16 +-- NOTICE.md | 14 +- README.md | 41 +++--- SECURITY.md | 3 +- charts/README.md | 12 +- charts/edc-controlplane/Chart.yaml | 2 +- charts/edc-controlplane/README.md | 5 +- charts/edc-controlplane/README.md.gotmpl | 1 + charts/edc-dataplane/Chart.yaml | 2 +- charts/edc-dataplane/README.md | 5 +- charts/edc-dataplane/README.md.gotmpl | 1 + docs/README.md | 27 +--- docs/development/Release.md | 11 +- docs/migration/Version_0.0.x_0.1.x.md | 87 +++--------- docs/migration/Version_0.1.0_0.1.1.md | 23 +--- docs/release-notes/Version 0.1.0.md | 10 +- docs/release-notes/Version 0.1.1.md | 11 +- docs/release-notes/Version 0.1.2.md | 3 +- edc-controlplane/README.md | 14 +- .../edc-controlplane-base/README.md | 2 +- .../edc-controlplane-memory/README.md | 86 ++++++------ .../README.md | 124 ++++++++--------- .../edc-controlplane-postgresql/README.md | 126 +++++++++--------- edc-dataplane/README.md | 3 +- .../edc-dataplane-azure-vault/README.md | 48 +++---- edc-dataplane/edc-dataplane-base/README.md | 2 +- .../edc-dataplane-hashicorp-vault/README.md | 48 +++---- .../business-partner-validation/README.md | 16 +-- edc-extensions/cx-oauth2/README.md | 24 ++-- edc-extensions/data-encryption/README.md | 16 +-- .../README.md | 13 +- edc-extensions/hashicorp-vault/README.md | 27 ++-- edc-extensions/postgresql-migration/README.md | 2 +- edc-tests/cucumber/README.md | 5 +- .../deployment/helm/omejdn/README.md | 3 +- 39 files changed, 465 insertions(+), 519 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c0f8fe3b0..4f74bf45e 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,28 +8,35 @@ assignees: '' --- ## Describe the bug + _A clear and concise description of what the bug is._ ### To Reproduce + _Steps to reproduce the behavior:_ + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error ### Expected behavior + _A clear and concise description of what you expected to happen._ ### Screenshots/Error Messages + _If applicable, add screenshots and/or error messages to help explain your problem._ ## Context Informations + _Add any other context about the probleme here._ - Used version: [e.g. Commit Hash] -- OS: [e.g. Mac OS (M1), Windows, Linux] -- Docker Version: [e.g. 20.10.12] -- `java --version`: +- OS: [e.g. Mac OS (M1), Windows, Linux] +- Docker Version: [e.g. 20.10.12] +- `java --version`: ## Possible Implementation + _You already know the root cause of the erroneous state and how to fix it? Feel free to share your thoughts._ diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 1b6f25b87..62c89ee8c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,8 +7,8 @@ assignees: '' --- -_If you are missing a feature or have an idea how to improve this project that should first be -discussed, please feel free to open up a [discussion](https://github.com/catenax-ng/catena-x-edc/discussions/categories/ideas)._ +_If you are missing a feature or have an idea how to improve this project that should first be +discussed, please feel free to open up a [discussion](https://github.com/eclipse-tractusx/tractusx-edc/discussions/categories/ideas)._ **Is your feature request related to a problem? Please describe.** _A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]_ diff --git a/CHANGELOG.md b/CHANGELOG.md index 173739461..3960f0c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,41 +130,31 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) - Bump s3 from 2.18.35 to 2.18.39 (#606) - -## [0.2.0] - 2022-12-15 +## [0.1.6] - 2023-02-20 ### Fixed -- Fixed Json LD serialization bug which prevented multi-BPN policies to be defined and used. Checkout the [docs](https://github.com/catenax-ng/product-edc/blob/0.2.0/edc-extensions/business-partner-validation/README.md) for more info. - -## [0.1.3] - 2022-11-30 - -### Added - -- New Postman collection for developers `/docs/development/postman` -- New EDC Image with HashiCorp Vault and InMemory Storage -- (Experimental) Simplified deployment of the EDC in `/charts/tractusx-connector` - -### Changed +- SQL leakage issue +- Catalog pagination -- Set EDC version to `0.0.1-20221006-SNAPSHOT` -- Business Partner Number Extension no longer supports the 'IN' constraint operator -- HashiCorp Vault Extension now allows sub directories for secrets -- Update package structure/namespace from `net.catenax` to `org.eclipse.tractusx` +## [0.1.5] - 2023-02-13 ### Fixed -- S3 Data Transfer +- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug +- Data Encryption extension: fixed usage of a blocking algorithm ## [0.1.2] - 2022-09-30 ### Added -- Introduced DEPENDENCIES file +- Introduced DEPENDENCIES file ### Changed -- Moved helm charts from `deployment/helm` to `charts` +- Moved helm charts from `deployment/helm` to `charts` +- Replaced distroless image with alpine in all docker images +- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` ## [0.1.1] - 2022-09-04 @@ -172,16 +162,16 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). ### Added -- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) +- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) ### Changed -- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) -- Helm Charts: TLS secret name is now configurable +- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) +- Helm Charts: TLS secret name is now configurable ### Fixed -- Connectors with Azure Vault extension are now starting again [link](https://github.com/eclipse-edc/Connector/issues/1892) +- Connectors with Azure Vault extension are now starting again [link](https://github.com/eclipse-edc/Connector/issues/1892) ## [0.1.0] - 2022-08-19 @@ -190,64 +180,64 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Added -- Control-Plane extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) - - run the EDC with multiple data planes at once -- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) - - add data plane instances to the control plane by configuration -- Data-Plane extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) - - transfer from and to AWS S3 buckets -- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) - - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) +- Control-Plane extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) + - run the EDC with multiple data planes at once +- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) + - add data plane instances to the control plane by configuration +- Data-Plane extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) + - transfer from and to AWS S3 buckets +- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) + - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) ### Changed -- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) -- EDC has been updated to version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - implications to the behavior of the connector have been covered in the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) +- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) +- EDC has been updated to version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - implications to the behavior of the connector have been covered in the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) ### Fixed -- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) -- Deletion of Asset becomes impossible when Contract Negotiation exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) -- Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) +- Deletion of Asset becomes impossible when Contract Negotiation exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) +- Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) ## [0.0.6] - 2022-07-29 ### Fixed -- Fixes [release 0.0.5](https://github.com/catenax-ng/product-edc/releases/tag/0.0.5), which introduced classpath issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) library +- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) library ## [0.0.5] - 2022-07-28 ### Added -- EDC Health Checks for HashiCorp Vault +- EDC Health Checks for HashiCorp Vault ### Changed -- BusinessPartnerNumber constraint supports List structure -- Helm: Confidential EDC settings can be set using k8s secrets -- HashiCorp Vault API path configurable +- BusinessPartnerNumber constraint supports List structure +- Helm: Confidential EDC settings can be set using k8s secrets +- HashiCorp Vault API path configurable ## [0.0.4] - 2022-06-27 ### Added -- HashiCorp Vault Extension -- Control Plane with HashiCorp Vault and PostgreSQL support +- HashiCorp Vault Extension +- Control Plane with HashiCorp Vault and PostgreSQL support ### Changed -- Release Worklow now publishes Product EDC Extensions as Maven Artifacts +- Release Workflow now publishes Product EDC Extensions as Maven Artifacts ### Fixed -- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 +- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 contract offers max. ### Removed -- CosmosDB Control Plane -- Control API Extension for all Control Planes +- CosmosDB Control Plane +- Control API Extension for all Control Planes ## [0.0.3] - 2022-05-23 @@ -255,28 +245,26 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ## [0.0.1] - 2022-05-13 -[Unreleased]: https://github.com/catenax-ng/product-edc/compare/0.3.0...HEAD - -[0.3.0]: https://github.com/catenax-ng/product-edc/compare/0.2.0...0.3.0 +[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.1.6...HEAD -[0.2.0]: https://github.com/catenax-ng/product-edc/compare/0.1.3...0.2.0 +[0.1.6]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.1.5...0.1.6 -[0.1.3]: https://github.com/catenax-ng/product-edc/compare/0.1.2...0.1.3 +[0.1.5]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.1.2...0.1.5 -[0.1.2]: https://github.com/catenax-ng/product-edc/compare/0.1.1...0.1.2 +[0.1.2]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.1.1...0.1.2 -[0.1.1]: https://github.com/catenax-ng/product-edc/compare/0.1.0...0.1.1 +[0.1.1]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.1.0...0.1.1 -[0.1.0]: https://github.com/catenax-ng/product-edc/compare/0.0.6...0.1.0 +[0.1.0]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.0.6...0.1.0 -[0.0.6]: https://github.com/catenax-ng/product-edc/compare/0.0.5...0.0.6 +[0.0.6]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.0.5...0.0.6 -[0.0.5]: https://github.com/catenax-ng/product-edc/compare/0.0.4...0.0.5 +[0.0.5]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.0.4...0.0.5 -[0.0.4]: https://github.com/catenax-ng/product-edc/compare/0.0.3...0.0.4 +[0.0.4]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.0.3...0.0.4 -[0.0.3]: https://github.com/catenax-ng/product-edc/compare/0.0.2...0.0.3 +[0.0.3]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.0.2...0.0.3 -[0.0.2]: https://github.com/catenax-ng/product-edc/compare/0.0.1...0.0.2 +[0.0.2]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.0.1...0.0.2 -[0.0.1]: https://github.com/catenax-ng/product-edc/compare/a02601306fed39a88a3b3b18fae98b80791157b9...0.0.1 +[0.0.1]: https://github.com/eclipse-tractusx/tractusx-edc/compare/a02601306fed39a88a3b3b18fae98b80791157b9...0.0.1 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 14db7e6fa..651d7656a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -11,19 +11,19 @@ In the interest of fostering an open and welcoming environment, we as community Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities @@ -43,4 +43,4 @@ Project committers or leaders who do not follow the Code of Conduct in good fait ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org) , version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) \ No newline at end of file +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org) , version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 39dd5bdba..7163eaf9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,33 +14,33 @@ where these companies will be able to participate quickly and with little IT infrastructure investment. Tractus-X is meant to be the PoC project of the Catena-X alliance focusing on parts traceability. -* https://projects.eclipse.org/projects/automotive.tractusx +* ## Developer resources Information regarding source code management, builds, coding standards, and more. -* https://projects.eclipse.org/projects/automotive.tractusx/developer +* The project maintains the source code repositories in the following GitHub organization: -* https://github.com/eclipse-tractusx/ +* ## Eclipse Development Process This Eclipse Foundation open project is governed by the Eclipse Foundation Development Process and operates under the terms of the Eclipse IP Policy. -* https://eclipse.org/projects/dev_process -* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf +* +* ## Eclipse Contributor Agreement In order to be able to contribute to Eclipse Foundation projects you must electronically sign the Eclipse Contributor Agreement (ECA). -* http://www.eclipse.org/legal/ECA.php +* The ECA provides the Eclipse Foundation with a permanent record that you agree that each of your contributions will comply with the commitments documented in @@ -49,10 +49,10 @@ the email address matching the "Author" field of your contribution's Git commits fulfills the DCO's requirement that you sign-off on your contributions. For more information, please see the Eclipse Committer Handbook: -https://www.eclipse.org/projects/handbook/#resources-commit + ## Contact Contact the project developers via the project's "dev" list. -* https://accounts.eclipse.org/mailing-list/tractusx-dev \ No newline at end of file +* diff --git a/NOTICE.md b/NOTICE.md index d9fce018c..4223c64f3 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -2,7 +2,7 @@ This content is produced and maintained by the Eclipse Tractus-X project. -* Project home: https://projects.eclipse.org/projects/automotive.tractusx +* Project home: See the AUTHORS file(s) distributed with this work for additional information regarding authorship. @@ -20,18 +20,16 @@ source code repository logs. 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 ## Source Code -The project maintains the following source code repositories -in the GitHub organization https://github.com/eclipse-tractusx: - -* https://github.com/eclipse-tractusx/ -* https://github.com/eclipse-tractusx/ +The project maintains the following source code repositories +in the GitHub organization : +* ## Third-party Content @@ -46,4 +44,4 @@ may have restrictions on the import, possession, and use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is -permitted. \ No newline at end of file +permitted. diff --git a/README.md b/README.md index 4bb5016de..0d9ef46e8 100644 --- a/README.md +++ b/README.md @@ -19,18 +19,17 @@

Container images and deployments of the Eclipse Dataspace Components open source project.
- Explore the docs » + Explore the docs »

View Eclipse Dataspace Components · - Releases + Releases · Report Bug / Request Feature

-
Table of Contents @@ -60,26 +59,26 @@ The project provides pre-built control- and data-plane [docker](https://www.dock ## Inventory The eclipse data space connector is split up into Control-Plane and Data-Plane, whereas the Control-Plane functions as administration layer -and has responsibility of resource management, contract negotiation and administer data transfer. +and has responsibility of resource management, contract negotiation and administer data transfer. The Data-Plane does the heavy lifting of transferring and receiving data streams. Depending on your environment there are different derivatives of the control-plane prepared: * [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) with dependency onto - * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) + * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) * [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) with dependency onto - * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) - * [PostgreSQL 8.2 or newer](https://www.postgresql.org/) + * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) + * [PostgreSQL 8.2 or newer](https://www.postgresql.org/) * [edc-controlplane-postgresql-hashicorp-vault](edc-controlplane/edc-controlplane-postgresql-hashicorp-vault) with dependency onto - * [Hashicorp Vault](https://www.vaultproject.io/) - * [PostgreSQL 8.2 or newer](https://www.postgresql.org/) + * [Hashicorp Vault](https://www.vaultproject.io/) + * [PostgreSQL 8.2 or newer](https://www.postgresql.org/) Derivatives of the Data-Plane can be found here * [edc-dataplane-azure-vault](edc-dataplane/edc-dataplane-azure-vault) with dependency onto - * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) + * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) * [edc-dataplane-hashicorp-vault](edc-dataplane/edc-dataplane-hashicorp-vault) with dependency onto - * [Hashicorp Vault](https://www.vaultproject.io/) + * [Hashicorp Vault](https://www.vaultproject.io/)

(back to top)

@@ -87,10 +86,10 @@ Derivatives of the Data-Plane can be found here

(back to top)

- ### Build Build Product-EDC together with its Container Images + ```shell ./gradlew dockerize ``` @@ -99,17 +98,17 @@ Build Product-EDC together with its Container Images ## License -Distributed under the Apache 2.0 License. See [LICENSE](https://github.com/catenax-ng/product-edc/blob/main/LICENSE) for more information. +Distributed under the Apache 2.0 License. See [LICENSE](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) for more information.

(back to top)

-[contributors-shield]: https://img.shields.io/github/contributors/catenax-ng/product-edc.svg?style=for-the-badge -[contributors-url]: https://github.com/catenax-ng/product-edc/graphs/contributors -[stars-shield]: https://img.shields.io/github/stars/catenax-ng/product-edc.svg?style=for-the-badge -[stars-url]: https://github.com/catenax-ng/product-edc/stargazers -[license-shield]: https://img.shields.io/github/license/catenax-ng/product-edc.svg?style=for-the-badge -[license-url]: https://github.com/catenax-ng/product-edc/blob/main/LICENSE -[release-shield]: https://img.shields.io/github/v/release/catenax-ng/product-edc.svg?style=for-the-badge -[release-url]: https://github.com/catenax-ng/product-edc/releases +[contributors-shield]: https://img.shields.io/github/contributors/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge +[contributors-url]: https://github.com/eclipse-tractusx/tractusx-edc/graphs/contributors +[stars-shield]: https://img.shields.io/github/stars/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge +[stars-url]: https://github.com/eclipse-tractusx/tractusx-edc/stargazers +[license-shield]: https://img.shields.io/github/license/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge +[license-url]: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE +[release-shield]: https://img.shields.io/github/v/release/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge +[release-url]: https://github.com/eclipse-tractusx/tractusx-edc/releases diff --git a/SECURITY.md b/SECURITY.md index 7d8fced73..eec5ca437 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,5 +2,4 @@ ## Reporting a Vulnerability -Please report a found vulnerability here: -[https://www.eclipse.org/security/](https://www.eclipse.org/security/) \ No newline at end of file +Please report a found vulnerability here: \ No newline at end of file diff --git a/charts/README.md b/charts/README.md index 1f453a962..adbaac6af 100644 --- a/charts/README.md +++ b/charts/README.md @@ -1,10 +1,12 @@ -# Chart Linting +# Helm Charts + +## Chart Linting Chart linting is performed using [helm's CT tool](https://github.com/helm/chart-testing). -Configuration files for [CT](../../ct.yaml), [Yamale](../../chart_schema.yaml) and [Yamllint](../../lintconf.yaml) have been provided. +Configuration files for [CT](../ct.yaml), [Yamale](../chart_schema.yaml) and [Yamllint](../lintconf.yaml) have been provided. -# Generate Chart Readme's +## Generate Chart Readme's To generate chart README.md files from its respective values.yaml file we use the [helm-docs tool](https://github.com/norwoodj/helm-docs): @@ -12,6 +14,6 @@ To generate chart README.md files from its respective values.yaml file we use th docker run --rm --volume "$(pwd):/helm-docs" -u $(id -u) jnorwood/helm-docs:v1.10.0 ``` -# Confidential EDC Settings +## Confidential EDC Settings -Some EDC settings should better not be part of the actual deployment (like credentials to the database or the vault). Therefore, it is possible to deploy a secret with these confidential settings beforehand, and make it known to the deployment by setting the secret name in the `envSecretName` field of the deployment. \ No newline at end of file +Some EDC settings should better not be part of the actual deployment (like credentials to the database or the vault). Therefore, it is possible to deploy a secret with these confidential settings beforehand, and make it known to the deployment by setting the secret name in the `envSecretName` field of the deployment. diff --git a/charts/edc-controlplane/Chart.yaml b/charts/edc-controlplane/Chart.yaml index 09c6201cc..e0ec00697 100644 --- a/charts/edc-controlplane/Chart.yaml +++ b/charts/edc-controlplane/Chart.yaml @@ -25,7 +25,7 @@ apiVersion: v2 name: edc-controlplane description: >- EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers -home: https://github.com/eclipse-tractusx/tractusx-edc +home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-controlplane type: application appVersion: "0.3.0" version: 0.3.0 diff --git a/charts/edc-controlplane/README.md b/charts/edc-controlplane/README.md index 71238a6ac..34b49b4e9 100644 --- a/charts/edc-controlplane/README.md +++ b/charts/edc-controlplane/README.md @@ -6,9 +6,10 @@ EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers -**Homepage:** +- **Homepage:** ## TL;DR + ```shell $ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev $ helm install my-release tractusx-edc/edc-controlplane --version 0.3.0 @@ -43,7 +44,7 @@ $ helm install my-release tractusx-edc/edc-controlplane --version 0.3.0 | envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-memory] | +| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-memory] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/charts/edc-controlplane/README.md.gotmpl b/charts/edc-controlplane/README.md.gotmpl index 1e026d9b4..022804eea 100644 --- a/charts/edc-controlplane/README.md.gotmpl +++ b/charts/edc-controlplane/README.md.gotmpl @@ -9,6 +9,7 @@ {{ template "chart.homepageLine" . }} ## TL;DR + ```shell $ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev $ helm install my-release tractusx-edc/edc-controlplane --version {{ .Version }} diff --git a/charts/edc-dataplane/Chart.yaml b/charts/edc-dataplane/Chart.yaml index e6c5c00bf..001fe2d1b 100644 --- a/charts/edc-dataplane/Chart.yaml +++ b/charts/edc-dataplane/Chart.yaml @@ -25,7 +25,7 @@ apiVersion: v2 name: edc-dataplane description: >- EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams -home: https://github.com/eclipse-tractusx/tractusx-edc +home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-dataplane type: application appVersion: "0.3.0" version: 0.3.0 diff --git a/charts/edc-dataplane/README.md b/charts/edc-dataplane/README.md index da5f4afd3..02a26f41d 100644 --- a/charts/edc-dataplane/README.md +++ b/charts/edc-dataplane/README.md @@ -6,9 +6,10 @@ EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams -**Homepage:** +- **Homepage:** ## TL;DR + ```shell $ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev $ helm install my-release tractusx-edc/edc-dataplane --version 0.3.0 @@ -39,7 +40,7 @@ $ helm install my-release tractusx-edc/edc-dataplane --version 0.3.0 | envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault] | +| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-azure-vault] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/charts/edc-dataplane/README.md.gotmpl b/charts/edc-dataplane/README.md.gotmpl index 3bed7d917..8411b344e 100644 --- a/charts/edc-dataplane/README.md.gotmpl +++ b/charts/edc-dataplane/README.md.gotmpl @@ -9,6 +9,7 @@ {{ template "chart.homepageLine" . }} ## TL;DR + ```shell $ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev $ helm install my-release tractusx-edc/edc-dataplane --version {{ .Version }} diff --git a/docs/README.md b/docs/README.md index ebcc9942c..096e41feb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,15 +1,16 @@ -# Product EDC +# Tractus-X EDC -The Catena-X Product EDC Repository creates runnable applications out of EDC extensions from the [Eclipse DataSpace Connector](https://github.com/eclipse-edc/Connector) repository. +The Tractus-X EDC repository creates runnable applications out of EDC extensions from the [Eclipse DataSpace Connector](https://github.com/eclipse-edc/Connector) repository. + +When running a EDC connector from the Product EDC repository there are three setups to choose from. They only vary by using different extensions for -When running a EDC connector from the Product EDC repository there are three setups to choose from. They only vary by using different extensions for - Resolving of Connector-Identities - Persistence of the Control-Plane-State - Persistence of Secrets (Vault) ## Connector Setup -The four supported setups are. +The three supported setups are. - Setup 1: In Memory & Azure Vault - [Control Plane](../edc-controlplane/edc-controlplane-memory/README.md) @@ -18,13 +19,6 @@ The four supported setups are. - [Azure Key Vault Extension](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/vault/azure-vault) - [Data Plane](../edc-dataplane/edc-dataplane-azure-vault/README.md) - [Azure Key Vault Extension](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/vault/azure-vault) -- Setup 2: In Memory & HashiCorp Vault -- [Control Plane](../edc-controlplane/edc-controlplane-memory/README.md) - - [IDS DAPS Extensions](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/iam/oauth2/daps) - - In Memory Persistence done by using no extension - - [HashiCorp Vault Extension](../edc-extensions/hashicorp-vault/README.md) -- [Data Plane](../edc-dataplane/edc-dataplane-azure-vault/README.md) - - [HashiCorp Vault Extension](../edc-extensions/hashicorp-vault/README.md) - Setup 2: PostgreSQL & Azure Vault - [Control Plane](../edc-controlplane/edc-controlplane-postgresql/README.md) - [IDS DAPS Extensions](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/iam/oauth2/daps) @@ -42,24 +36,17 @@ The four supported setups are. ## Recommended Documentation -**This Repository** +### This Repository - [Update EDC Version from 0.0.x - 0.1.x](migration/Version_0.0.x_0.1.x.md) - [Application: Control Plane](../edc-controlplane) - [Application: Data Plane](../edc-dataplane) - [Extension: Business Partner Numbers](../edc-extensions/business-partner-validation/README.md) -- [Example: Connector Configuration (Helm)](../edc-tests/src/main/resources/deployment/helm/all-in-one/README.md) - [Example: Local TXDC Setup](samples/Local%20TXDC%20Setup.md) - [Example: Data Transfer](samples/Transfer%20Data.md) -**Eclipse Dataspace Connector** +### Eclipse Dataspace Connector - [EDC Domain Model](https://github.com/eclipse-edc/Connector/blob/main/docs/developer/architecture/domain-model.md) - [EDC Open API Spec](https://github.com/eclipse-edc/Connector/blob/main/resources/openapi/openapi.yaml) - [HTTP Receiver Extension](https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/http-receiver) - -**Catena-X** - -_Only accessible for Catena-X Members._ - -- [DAPS](https://confluence.catena-x.net/display/ARTI/Connector+Configuration) diff --git a/docs/development/Release.md b/docs/development/Release.md index 8628bddfa..ded1e4a8b 100644 --- a/docs/development/Release.md +++ b/docs/development/Release.md @@ -13,15 +13,15 @@ project's [GitHub page](https://github.com/eclipse/dash-licenses#get-it). ### 2. Generate DEPENDENCIES file -This call generates the dependencies file. This list is populated by deriving dependencies using the build tool (i.e., +The following call generates the dependencies file. This list is populated by deriving dependencies using the build tool (i.e., gradle), analysing them using an IP tool (i.e., Eclipse Dash Tool), and decorating the resulting report with additional information using a custom script. Execute the gradle task `allDependencies` for creating an integrated dependency report over all sub-modules of the project (including isolated modules). To process the dependencies of a specific module (e.g., an individual launcher) -execute the standard `dependencies` task: +execute the standard `dependencies` task. -- First, the dependencies of this module are calculated with gradle and passed to the Dash tool: +First, the dependencies of this module are calculated with gradle and passed to the Dash tool: ```shell gradle allDependencies | grep -Poh "(?<=\s)[\w.-]+:[\w.-]+:[^:\s]+" | sort | uniq | java -jar /path/org.eclipse.dash.licenses-0.0.1-SNAPSHOT.jar - -summary DEPENDENCIES @@ -34,10 +34,9 @@ _Note: on some machines (e.g. macOS) [the ack tool](https://beyondgrep.com/insta If a dependency is `restricted`, it is not approved by the Eclipse Foundation, yet. The Eclipse Bot is able to approve dependencies automatically, if the license can be resolved by ClearlyDefined. -1. (optional) Visit [https://clearlydefined.io/harvest](https://clearlydefined.io/harvest) and harvest the dependency +1. (optional) Visit and harvest the dependency from maven central. 2. Create the Eclipse IP Issues or ask an Eclipse Commiter to do this for you. [maven-shield]: https://img.shields.io/badge/Apache%20Maven-URL-blue - -[maven-url]: https://maven.apache.org \ No newline at end of file +[maven-url]: https://maven.apache.org diff --git a/docs/migration/Version_0.0.x_0.1.x.md b/docs/migration/Version_0.0.x_0.1.x.md index e6c4539d9..353db9368 100644 --- a/docs/migration/Version_0.0.x_0.1.x.md +++ b/docs/migration/Version_0.0.x_0.1.x.md @@ -6,7 +6,7 @@ This document contains a list of breaking changes that are introduced in version 1. PostgreSQL Database 1. Criteria in Policy & Contract Definitions Table - 2. Delete Contract Agreements + 2. Delete Contract Agreements 2. Data Management API 1. Policy Path 2. Policy Payload @@ -27,14 +27,9 @@ be done by the user itself. Criteria in Policies and Contract Definitions are serialized as JSON and put into the database. The Criteria schema changed and already existing database entries will cause _NullPointerExceptions_. - - -
- Example Exception - #### Example Exception -``` +```plain [2022-08-02 09:32:37] [SEVERE ] Could not handle multipart request: null org.eclipse.dataspaceconnector.spi.EdcException at org.eclipse.dataspaceconnector.transaction.local.LocalTransactionContext.execute(LocalTransactionContext.java:70) @@ -122,13 +117,7 @@ Caused by: java.lang.NullPointerException ... 69 more ``` -
- -
- - Solution 1: Update all Criteria manually - -#### Update all Criteria manually +#### Solution 1: Update all Criteria manually Root of this issue is that the operator, left- and right-operand Criteria field names changed. @@ -141,23 +130,17 @@ Root of this issue is that the operator, left- and right-operand Criteria field It is possible to resolve this issue by updating the content of the column, that contain JSON serialized constraints, from -``` +```json {"criteria":[{"left":"asset:prop:id","op":"=","right":"asset-1"}]} ``` to -``` +```json {"criteria":[{"operandLeft":"asset:prop:id","operator":"=","operandRight":"asset-1"}]} ``` -
- -
- - Solution 2: Delete all rows containing Constraints - -#### Delete all rows containing Criteria +#### Solution 2: Delete all rows containing Constraints Instead of updating each row in the database it's also possible to delete all Contract Definitions and Policies. Additionally it's necessary to delete all Negotiations, as they might reference existing Contract Definitions and/or @@ -166,7 +149,7 @@ Policies. Theoretically it's also necessary to delete Contract Agreements. As their deletion is already described in another section, we can skip them here. -**Required Queries** +##### Required Queries ```sql DELETE @@ -183,23 +166,18 @@ DELETE FROM edc_policydefinitins; ``` -
- ### 1.2 Delete Contract Agreements In the new version contract agreement rows contain a serialized policy at the time, the contract was concluded. With the EDC update all existing Contract Agreements must be deleted. -
- Required Query +#### Required Query ```sql DELETE FROM edc_contract_agreement; ``` -
- ## 2. Data Management API It might be necessary to update applications and scripts that use the Data Management API. This section covers the most @@ -210,26 +188,17 @@ important changes in endpoints and payloads. The Data Management API Path for Policies changes from `/policies` to `/policydefinitions`. -
- Example Call - #### Get All Policies ```bash curl -X GET "${DATA_MGMT_ENDPOINT}/data/policydefinitions" --header "X-Api-Key: " --header "Content-Type: application/json" ``` -
- ### 2.2 Policy Payload The Policy Payload now wraps the policy details in an additional policy object. -
- -Payload Comparison - -**New Payload** +#### New Payload ```json { @@ -242,7 +211,7 @@ The Policy Payload now wraps the policy details in an additional policy object. } ``` -**Old Payload** +#### Old Payload ```json { @@ -253,46 +222,36 @@ The Policy Payload now wraps the policy details in an additional policy object. } ``` -
- ### 2.3 Criteria in Payload of Contract Definitions and Policies The payload of a Policy or a Contract Definition may contain one or more Criteria. The format of these serialized Criteria changed. Please note that there is no input validation, that detects errors when the old Criteria format is used! -
+#### Old Criterion Format -Criterion Format Change - -**Old Criterion Format** -``` +```json { "left": "asset:prop:id", "op": "=", "right": "1" } ``` -**New Criterion Format** -``` +#### New Criterion Format + +```json { "operandLeft": "asset:prop:id", "operator": "=", "operandRight": "1" } ``` -**Example Call** +#### Example Call ```bash curl -X POST "${DATA_MGMT_ENDPOINT}/data/contractdefinitions" --header "X-Api-Key: " --header "Content-Type: application/json" --data "{ \"id\": \"1\", \"criteria\": [ { \"operandLeft\": \"asset:prop:id\", \"operator\": \"=\", \"operandRight\": \"1\" } ], \"accessPolicyId\": \"1\", \"contractPolicyId\": \"1\" }" ``` -
- ### 2.4 Data Address When using a Data Address of type `HttpData` please notice that the property `endpoint` changed to `baseUrl`. This property is mostly used when creating assets. +#### Old Asset format -
- -DataAddress Comparison - -**Old Asset format**: ```json { "asset": { @@ -307,7 +266,8 @@ property is mostly used when creating assets. } ``` -**New Asset format**: +#### New Asset format + ```json { "asset": { @@ -321,18 +281,13 @@ property is mostly used when creating assets. } } ``` -
-
- -Example Call +#### Example Call ```bash curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Product EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' ``` -
- ## 3. Connector Configuration ### 3.1 Token Validation Endpoint Setting @@ -346,4 +301,4 @@ With this version a new feature was introduced which allows to have separate Dat transfer-flows (HttpProxy, S3, etc.). The Catena-X EDC team has additionally a new extension created which allows a simpler registration of additional dataplanes. Therefor some changes needs to be applied. Further documentation can be found in the extension folder: -[dataplane-selector-configuration](/edc-extensions/dataplane-selector-configuration/README.md) +[dataplane-selector-configuration](../../edc-extensions/dataplane-selector-configuration/README.md) diff --git a/docs/migration/Version_0.1.0_0.1.1.md b/docs/migration/Version_0.1.0_0.1.1.md index 5797593de..528dc8c37 100644 --- a/docs/migration/Version_0.1.0_0.1.1.md +++ b/docs/migration/Version_0.1.0_0.1.1.md @@ -16,7 +16,6 @@ Due to a change in the DAPS authentication mechanism this version cannot exchang 2. Connector Configuration 1. CX OAuth Extension - ## 1. Data Management API It might be necessary to update applications and scripts that use the Data Management API. This section covers the most @@ -26,11 +25,8 @@ important changes in endpoints and payloads. The id field of the PolicyDefinition was renamed from `uid` to `id`. -
- -Example +#### Old Call -Old Call ```json { "uid": "1", @@ -50,7 +46,8 @@ Old Call } ``` -New call +#### New call + ```json { "id": "1", @@ -70,22 +67,16 @@ New call } ``` -
- ## 2. Connector Configuration + ### 2.1. CX OAuth Extension All connectors are now shipped with a new OAuth extension. This extension has an additional mandatory setting called `edc.ids.endpoint.audience`, that must be set to the IDS path. -[Documentation](/edc-extensions/cx-oauth2/README.md) +[Documentation](../../edc-extensions/cx-oauth2/README.md) +#### Example -
- -Example - -``` +```properties edc.ids.endpoint.audience=http://plato-edc-controlplane:8282/api/v1/ids/data ``` - -
diff --git a/docs/release-notes/Version 0.1.0.md b/docs/release-notes/Version 0.1.0.md index 9cf96c304..4f872ff4e 100644 --- a/docs/release-notes/Version 0.1.0.md +++ b/docs/release-notes/Version 0.1.0.md @@ -1,8 +1,9 @@ # Release Notes Version 0.1.0 + 19.08.2022 > **BREAKING CHANGES** -> +> > When upgrading from version 0.0.x please consolidate the migration documentation before ([link](../migration/Version_0.0.x_0.1.x.md)). ## 0. Summary @@ -19,11 +20,10 @@ Upgraded the Eclipse Dataspace Connector Extensions to version 0.0.1-20220818-SNAPSHOT. Please be aware that this introduces some breaking changes. Code Repository -https://github.com/eclipse-dataspaceconnector/DataSpaceConnector + Snapshot Artifact Repository -https://oss.sonatype.org/#nexus-search;quick~org.eclipse.dataspaceconnector - + ## 2. New Extensions @@ -61,4 +61,4 @@ This section covers the most relevant bug fixes, included in this version. - Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1410)) -- DataAddress is passed unencrypted from DataProvider to DataConsumer ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1504)) \ No newline at end of file +- DataAddress is passed unencrypted from DataProvider to DataConsumer ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1504)) diff --git a/docs/release-notes/Version 0.1.1.md b/docs/release-notes/Version 0.1.1.md index a56d1f307..5138b8b4d 100644 --- a/docs/release-notes/Version 0.1.1.md +++ b/docs/release-notes/Version 0.1.1.md @@ -1,11 +1,11 @@ # Release Notes Version 0.1.1 -31.08.2022 +31.08.2022 > **BREAKING CHANGES** -> +> > Please consolidate the migration documentation ([link](../migration/Version_0.1.0_0.1.1.md)). - +> > **Important Notice** > > The **InMemoryControlPlane** image is broken. Please use another control plane instead. @@ -33,10 +33,9 @@ Using the open source OAuth Extension it is possible for a connector to re-use a [Documentation](../../edc-extensions/cx-oauth2/README.md) +#### New Audience Configuration -**New Audience Configuration** - -``` +```properties edc.ids.endpoint.audience=http://plato-edc-controlplane:8282/api/v1/ids/data ``` diff --git a/docs/release-notes/Version 0.1.2.md b/docs/release-notes/Version 0.1.2.md index 812e8a1d7..cef41cbd6 100644 --- a/docs/release-notes/Version 0.1.2.md +++ b/docs/release-notes/Version 0.1.2.md @@ -1,4 +1,5 @@ # Release Notes Version 0.1.2 + 30.09.2022 > This version introduced mostly bugfixes and thread mitigation by updating libraries. @@ -17,4 +18,4 @@ Introduce alpine image as base for all Product EDC Images (replaced distroless i - Contract negotiation not working when initiated with policy id ([issue](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/1251)) -- Negotiation of Policies with extensible properties now works as expected \ No newline at end of file +- Negotiation of Policies with extensible properties now works as expected diff --git a/edc-controlplane/README.md b/edc-controlplane/README.md index 01017989c..3f59218d5 100644 --- a/edc-controlplane/README.md +++ b/edc-controlplane/README.md @@ -11,6 +11,7 @@ The only API that is protected by some kind of security mechanism is the Data Ma The key value must be configured in `edc.api.auth.key`. All requests to the Data Management API must have `X-Api-Key` header with the key value. Example: + ```bash curl -X GET --header "X-Api-Key: " ``` @@ -22,28 +23,31 @@ curl -X GET --header "X-Api-Key: " Please be aware that there are several confidential settings, that should not be part of the actual EDC configuration file. Some of these confidential settings are + - Vault credentials - Data Management API key - Database credentials As it is possible to configure EDC settings via environment variables, one way to do it would be via Kubernetes Secrets. For other deployment scenarios than Kubernetes equivalent measures should be taken. -# Known Control Plane Issues +## Known Control Plane Issues Please have a look at the open issues in the open source repository. The list below might not be maintained well and only contains the most important issues. -EDC Github Repository https://github.com/eclipse-edc/Connector/issues +EDC GitHub Repository --- **Please note** that some of these issues might already be fixed on the EDC main branch, but are not part of the specific -EDC commit the Product-EDC uses. +EDC commit the Tractus-X-EDC uses. --- -**Persistence** +### Persistence + - ContractDefinition-AssetSelector of InMemory Connector selects 50 Asset max.([issue](https://github.com/eclipse-edc/Connector/issues/1779)) -**Other** +### Other + - Non-IDS-Transformable-ContractDefinition causes connector to be unable to send out self-descriptions/catalogs([issue](https://github.com/eclipse-edc/Connector/issues/1265)) - **Workaround:** Delete non-transformable ContractDefinition or Policy. diff --git a/edc-controlplane/edc-controlplane-base/README.md b/edc-controlplane/edc-controlplane-base/README.md index 9fe217c80..269de27ca 100644 --- a/edc-controlplane/edc-controlplane-base/README.md +++ b/edc-controlplane/edc-controlplane-base/README.md @@ -1,6 +1,6 @@ # EDC Control-Plane Base Module -### Building +## Building ```shell ./gradlew edc-controlplane:edc-controlplane-base:build diff --git a/edc-controlplane/edc-controlplane-memory/README.md b/edc-controlplane/edc-controlplane-memory/README.md index 2eb2ce2e4..ca1f0bef7 100644 --- a/edc-controlplane/edc-controlplane-memory/README.md +++ b/edc-controlplane/edc-controlplane-memory/README.md @@ -1,52 +1,52 @@ # EDC Control-Plane backed by In-Memory Stores -### Building +## Building ```shell ./gradlew :edc-controlplane:edc-controlplane-memory:dockerize ``` -### Configuration (configuration.properties) +## Configuration (configuration.properties) Listed below are configuration keys needed to get the `edc-controlplane-memory` up and running. Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). -| Key | Required | Example | Description | -|--- |--- |--- |--- | -| edc.api.auth.key | | password | default value: random UUID | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.data.port | X | 8181 | | -| web.http.data.path | X | /data | | -| web.http.validation.port | X | 8182 | | -| web.http.validation.path | X | /validation | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| web.http.ids.port | X | 8282 | | -| web.http.ids.path | X | /api/v1/ids | | -| edc.receiver.http.endpoint | X | http://backend-service | | -| edc.ids.title | | Eclipse Dataspace Connector | | -| edc.ids.description | | Eclipse Dataspace Connector | | -| edc.ids.id | | urn:connector:edc | | -| edc.ids.security.profile | | base | | -| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | -| edc.ids.maintainer | | http://localhost | | -| edc.ids.curator | | http://localhost | | -| edc.ids.catalog.id | | urn:catalog:default | | -| ids.webhook.address | | http://localhost:8282/api/v1/ids | | -| edc.hostname | | localhost | | -| edc.oauth.token.url | X | https://daps.catena-x.net | | -| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | -| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | -| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.name | X | my-vault-name | | -| edc.vault.clientsecret | X | 34-chars-secret | | -| edc.transfer.proxy.endpoint | X | | | -| edc.transfer.proxy.token.signer.privatekey.alias | X | | | - -#### Example configuration.properties +| Key | Required | Example | Description | +|--------------------------------------------------|----------|--------------------------------------|----------------------------| +| edc.api.auth.key | | password | default value: random UUID | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | /data | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | | | +| edc.ids.maintainer | | | | +| edc.ids.curator | | | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | | | +| edc.hostname | | localhost | | +| edc.oauth.token.url | X | | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | +| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.name | X | my-vault-name | | +| edc.vault.clientsecret | X | 34-chars-secret | | +| edc.transfer.proxy.endpoint | X | | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | | | + +### Example configuration.properties JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. @@ -100,7 +100,8 @@ edc.transfer.proxy.token.signer.privatekey.alias=azure-vault-token-signer-privat EOF ``` -#### Example logging.properties +### Example logging.properties + ```shell # Create logging.properties export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) @@ -114,7 +115,8 @@ java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [ EOF ``` -#### Example opentelemetry.properties +### Example opentelemetry.properties + ```shell # Create opentelemetry.properties export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) @@ -124,7 +126,7 @@ otel.javaagent.debug=false EOF ``` -### Running +## Running ```shell docker run \ @@ -133,4 +135,4 @@ docker run \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ -i edc-controlplane-memory:latest -``` \ No newline at end of file +``` diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md index 0efd61884..636d8a8b8 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md @@ -1,71 +1,71 @@ # EDC Control-Plane backed by [Postgresql](https://www.postgresql.org/) and [HashiCorp vault](https://www.vaultproject.io/docs) -### Building +## Building ```shell ./gardlew :edc-controlplane:edc-controlplane-postgresql-hashicorp-vault:dockerize ``` -### Configuration +## Configuration Listed below are configuration keys needed to get the `edc-controlplane-postgresql-hashicorp-vault` up and running. Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). -| Key | Required | Example | Description | -|--- |--- |--- |--- | -| edc.api.auth.key | | password | default value: random UUID | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.data.port | X | 8181 | | -| web.http.data.path | X | /data | | -| web.http.validation.port | X | 8182 | | -| web.http.validation.path | X | /validation | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| web.http.ids.port | X | 8282 | | -| web.http.ids.path | X | /api/v1/ids | | -| edc.receiver.http.endpoint | X | http://backend-service | | -| edc.ids.title | | Eclipse Dataspace Connector | | -| edc.ids.description | | Eclipse Dataspace Connector | | -| edc.ids.id | | urn:connector:edc | | -| edc.ids.security.profile | | base | | -| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | -| edc.ids.maintainer | | http://localhost | | -| edc.ids.curator | | http://localhost | | -| edc.ids.catalog.id | | urn:catalog:default | | -| ids.webhook.address | | http://localhost:8282/api/v1/ids | | -| edc.hostname | | localhost | | -| edc.oauth.token.url | X | https://daps.catena-x.net | | -| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | -| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.hashicorp.url | X | http://vault | | -| edc.vault.hashicorp.token | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.hashicorp.timeout.seconds | | 30 | | -| edc.datasource.asset.name | X | asset | | -| edc.datasource.asset.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset_db | | -| edc.datasource.asset.user | X | username | | -| edc.datasource.asset.password | X | password | | -| edc.datasource.contractdefinition.name | X | contractdefinition | | -| edc.datasource.contractdefinition.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition_db | | -| edc.datasource.contractdefinition.user | X | username | | -| edc.datasource.contractdefinition.password | X | password | | -| edc.datasource.contractnegotiation.name | X | contractnegotiation | | -| edc.datasource.contractnegotiation.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation_db | | -| edc.datasource.contractnegotiation.user | X | username | | -| edc.datasource.contractnegotiation.password | X | password | | -| edc.datasource.policy.name | X | policy | | -| edc.datasource.policy.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy_db | | -| edc.datasource.policy.user | X | username | | -| edc.datasource.policy.password | X | password | | -| edc.datasource.transferprocess.name | X | transferprocess | | -| edc.datasource.transferprocess.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess_db | | -| edc.datasource.transferprocess.user | X | username | | -| edc.datasource.transferprocess.password | X | password | | -| edc.transfer.proxy.endpoint | X | http://proxy | | -| edc.transfer.proxy.token.signer.privatekey.alias | X | | | - -#### Example configuration.properties +| Key | Required | Example | Description | +|--------------------------------------------------|----------|------------------------------------------------------------------------------|----------------------------| +| edc.api.auth.key | | password | default value: random UUID | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | /data | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | | | +| edc.ids.maintainer | | | | +| edc.ids.curator | | | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | | | +| edc.hostname | | localhost | | +| edc.oauth.token.url | X | | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.hashicorp.url | X | | | +| edc.vault.hashicorp.token | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.hashicorp.timeout.seconds | | 30 | | +| edc.datasource.asset.name | X | asset | | +| edc.datasource.asset.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset_db | | +| edc.datasource.asset.user | X | username | | +| edc.datasource.asset.password | X | password | | +| edc.datasource.contractdefinition.name | X | contractdefinition | | +| edc.datasource.contractdefinition.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition_db | | +| edc.datasource.contractdefinition.user | X | username | | +| edc.datasource.contractdefinition.password | X | password | | +| edc.datasource.contractnegotiation.name | X | contractnegotiation | | +| edc.datasource.contractnegotiation.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation_db | | +| edc.datasource.contractnegotiation.user | X | username | | +| edc.datasource.contractnegotiation.password | X | password | | +| edc.datasource.policy.name | X | policy | | +| edc.datasource.policy.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy_db | | +| edc.datasource.policy.user | X | username | | +| edc.datasource.policy.password | X | password | | +| edc.datasource.transferprocess.name | X | transferprocess | | +| edc.datasource.transferprocess.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess_db | | +| edc.datasource.transferprocess.user | X | username | | +| edc.datasource.transferprocess.password | X | password | | +| edc.transfer.proxy.endpoint | X | | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | | | + +### Example configuration.properties JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. @@ -140,7 +140,8 @@ edc.datasource.transferprocess.password=pass EOF ``` -#### Example logging.properties +### Example logging.properties + ```shell # Create logging.properties export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) @@ -154,7 +155,8 @@ java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [ EOF ``` -#### Example opentelemetry.properties +### Example opentelemetry.properties + ```shell # Create opentelemetry.properties export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) @@ -164,7 +166,7 @@ otel.javaagent.debug=false EOF ``` -### Running +## Running ```shell docker run \ @@ -173,4 +175,4 @@ docker run \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ -i edc-controlplane-postgresql-hashicorp-vault:latest -``` \ No newline at end of file +``` diff --git a/edc-controlplane/edc-controlplane-postgresql/README.md b/edc-controlplane/edc-controlplane-postgresql/README.md index bb8730712..b9ec0afd0 100644 --- a/edc-controlplane/edc-controlplane-postgresql/README.md +++ b/edc-controlplane/edc-controlplane-postgresql/README.md @@ -1,72 +1,72 @@ # EDC Control-Plane backed by [Postgresql](https://www.postgresql.org/) -### Building +## Building ```shell ./gardlew :edc-controlplane:edc-controlplane-postgresql:dockerize ``` -### Configuration +## Configuration Listed below are configuration keys needed to get the `edc-controlplane-postgresql` up and running. Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). -| Key | Required | Example | Description | -|--- |--- |--- |--- | -| edc.api.auth.key | | password | default value: random UUID | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.data.port | X | 8181 | | -| web.http.data.path | X | | | -| web.http.validation.port | X | 8182 | | -| web.http.validation.path | X | /validation | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| web.http.ids.port | X | 8282 | | -| web.http.ids.path | X | /api/v1/ids | | -| edc.receiver.http.endpoint | X | http://backend-service | | -| edc.ids.title | | Eclipse Dataspace Connector | | -| edc.ids.description | | Eclipse Dataspace Connector | | -| edc.ids.id | | urn:connector:edc | | -| edc.ids.security.profile | | base | | -| edc.ids.endpoint | | http://localhost:8282/api/v1/ids | | -| edc.ids.maintainer | | http://localhost | | -| edc.ids.curator | | http://localhost | | -| edc.ids.catalog.id | | urn:catalog:default | | -| ids.webhook.address | | http://localhost:8282/api/v1/ids | | -| edc.hostname | | localhost | | -| edc.oauth.token.url | X | https://daps.catena-x.net | | -| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | -| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | -| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.name | X | my-vault-name | | -| edc.vault.clientsecret | X | 34-chars-secret | | -| edc.datasource.asset.name | X | asset | | -| edc.datasource.asset.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset_db | | -| edc.datasource.asset.user | X | username | | -| edc.datasource.asset.password | X | password | | -| edc.datasource.contractdefinition.name | X | contractdefinition | | -| edc.datasource.contractdefinition.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition_db | | -| edc.datasource.contractdefinition.user | X | username | | -| edc.datasource.contractdefinition.password | X | password | | -| edc.datasource.contractnegotiation.name | X | contractnegotiation | | -| edc.datasource.contractnegotiation.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation_db | | -| edc.datasource.contractnegotiation.user | X | username | | -| edc.datasource.contractnegotiation.password | X | password | | -| edc.datasource.policy.name | X | policy | | -| edc.datasource.policy.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy_db | | -| edc.datasource.policy.user | X | username | | -| edc.datasource.policy.password | X | password | | -| edc.datasource.transferprocess.name | X | transferprocess | | -| edc.datasource.transferprocess.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess_db | | -| edc.datasource.transferprocess.user | X | username | | -| edc.datasource.transferprocess.password | X | password | | -| edc.transfer.proxy.endpoint | X | | | -| edc.transfer.proxy.token.signer.privatekey.alias | X | | | - -#### Example configuration.properties +| Key | Required | Example | Description | +|--------------------------------------------------|----------|------------------------------------------------------------------------------|----------------------------| +| edc.api.auth.key | | password | default value: random UUID | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | | | +| edc.ids.maintainer | | | | +| edc.ids.curator | | | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | | | +| edc.hostname | | localhost | | +| edc.oauth.token.url | X | | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | +| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.name | X | my-vault-name | | +| edc.vault.clientsecret | X | 34-chars-secret | | +| edc.datasource.asset.name | X | asset | | +| edc.datasource.asset.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset_db | | +| edc.datasource.asset.user | X | username | | +| edc.datasource.asset.password | X | password | | +| edc.datasource.contractdefinition.name | X | contractdefinition | | +| edc.datasource.contractdefinition.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition_db | | +| edc.datasource.contractdefinition.user | X | username | | +| edc.datasource.contractdefinition.password | X | password | | +| edc.datasource.contractnegotiation.name | X | contractnegotiation | | +| edc.datasource.contractnegotiation.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation_db | | +| edc.datasource.contractnegotiation.user | X | username | | +| edc.datasource.contractnegotiation.password | X | password | | +| edc.datasource.policy.name | X | policy | | +| edc.datasource.policy.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy_db | | +| edc.datasource.policy.user | X | username | | +| edc.datasource.policy.password | X | password | | +| edc.datasource.transferprocess.name | X | transferprocess | | +| edc.datasource.transferprocess.url | X | jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess_db | | +| edc.datasource.transferprocess.user | X | username | | +| edc.datasource.transferprocess.password | X | password | | +| edc.transfer.proxy.endpoint | X | | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | | | + +### Example configuration.properties JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. @@ -143,7 +143,8 @@ edc.datasource.transferprocess.password=pass EOF ``` -#### Example logging.properties +### Example logging.properties + ```shell # Create logging.properties export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) @@ -157,7 +158,8 @@ java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [ EOF ``` -#### Example opentelemetry.properties +### Example opentelemetry.properties + ```shell # Create opentelemetry.properties export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) @@ -167,7 +169,7 @@ otel.javaagent.debug=false EOF ``` -### Running +## Running ```shell docker run \ @@ -176,4 +178,4 @@ docker run \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ -i edc-controlplane-postgresql:latest -``` \ No newline at end of file +``` diff --git a/edc-dataplane/README.md b/edc-dataplane/README.md index 2deeec0d6..9ca28b38d 100644 --- a/edc-dataplane/README.md +++ b/edc-dataplane/README.md @@ -11,5 +11,6 @@ Please be aware that there are several confidential settings, that should not be As it is possible to configure EDC settings via environment variables, one way to do it would be via Kubernetes Secrets. For other deployment scenarios than Kubernetes equivalent measures should be taken. -# Known Data Plane Issues +## Known Data Plane Issues + Please have a look at the open issues in the open source repository: [EDC Github Repository](https://github.com/eclipse-edc/Connector/issues) diff --git a/edc-dataplane/edc-dataplane-azure-vault/README.md b/edc-dataplane/edc-dataplane-azure-vault/README.md index b133fee26..564aabde6 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/README.md +++ b/edc-dataplane/edc-dataplane-azure-vault/README.md @@ -1,34 +1,34 @@ # EDC Data-Plane with [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) -### Building +## Building ```shell ./gardlew :edc-dataplane:edc-dataplane-azure-vault:dockerize ``` -### Configuration +## Configuration Listed below are configuration keys needed to get the `edc-dataplane-azure-vault` up and running. Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). -| Key | Required | Example | Description | -|--- |--- |--- |--- | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.public.port | X | 8181 | | -| web.http.public.path | X | | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| edc.receiver.http.endpoint | X | http://backend-service | | -| edc.hostname | | localhost | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | -| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.name | X | my-vault-name | | -| edc.vault.clientsecret | X | 34-chars-secret | | -| edc.dataplane.token.validation.endpoint | X | http://controlplane:8182/validation/token | | - -#### Example configuration.properties +| Key | Required | Example | Description | +|-----------------------------------------|----------|---------------------------------------------|-------------| +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.public.port | X | 8181 | | +| web.http.public.path | X | | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| edc.receiver.http.endpoint | X | | | +| edc.hostname | | localhost | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | +| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.name | X | my-vault-name | | +| edc.vault.clientsecret | X | 34-chars-secret | | +| edc.dataplane.token.validation.endpoint | X | | | + +### Example configuration.properties JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. @@ -58,7 +58,8 @@ edc.vault.clientsecret=34-chars-secret EOF ``` -#### Example logging.properties +### Example logging.properties + ```shell # Create logging.properties export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) @@ -72,7 +73,8 @@ java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [ EOF ``` -#### Example opentelemetry.properties +### Example opentelemetry.properties + ```shell # Create opentelemetry.properties export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) @@ -82,7 +84,7 @@ otel.javaagent.debug=false EOF ``` -### Running +## Running ```shell docker run \ diff --git a/edc-dataplane/edc-dataplane-base/README.md b/edc-dataplane/edc-dataplane-base/README.md index ee8ac9961..89ec91506 100644 --- a/edc-dataplane/edc-dataplane-base/README.md +++ b/edc-dataplane/edc-dataplane-base/README.md @@ -1,6 +1,6 @@ # EDC Data-Plane Base Module -### Building +## Building ```shell ./gardlew :edc-dataplane:edc-dataplane-base:build diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/README.md b/edc-dataplane/edc-dataplane-hashicorp-vault/README.md index f43382ee1..9930c13a8 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/README.md +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/README.md @@ -1,33 +1,33 @@ # EDC Data-Plane [Hashicorp Vault](https://www.vaultproject.io/) -### Building +## Building ```shell ./gardlew :edc-dataplane:edc-dataplane-hashicorp-vault:dockerize ``` -### Configuration +## Configuration Listed below are configuration keys needed to get the `edc-dataplane-hashicorp-vault` up and running. Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). -| Key | Required | Example | Description | -|--- |--- |--- |--- | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.public.port | X | 8181 | | -| web.http.public.path | X | | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| edc.receiver.http.endpoint | X | http://backend-service | | -| edc.hostname | | localhost | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.hashicorp.url | X | http://vault | | -| edc.vault.hashicorp.token | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.hashicorp.timeout.seconds | | 30 | | -| edc.dataplane.token.validation.endpoint | X | http://controlplane:8182/validation/token | | - -#### Example configuration.properties +| Key | Required | Example | Description | +|-----------------------------------------|----------|---------------------------------------------|-------------| +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.public.port | X | 8181 | | +| web.http.public.path | X | | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| edc.receiver.http.endpoint | X | | | +| edc.hostname | | localhost | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.vault.hashicorp.url | X | | | +| edc.vault.hashicorp.token | X | 55555555-6666-7777-8888-999999999999 | | +| edc.vault.hashicorp.timeout.seconds | | 30 | | +| edc.dataplane.token.validation.endpoint | X | | | + +### Example configuration.properties JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. @@ -56,7 +56,8 @@ edc.vault.hashicorp.timeout.seconds=30 EOF ``` -#### Example logging.properties +### Example logging.properties + ```shell # Create logging.properties export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) @@ -70,7 +71,8 @@ java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [ EOF ``` -#### Example opentelemetry.properties +### Example opentelemetry.properties + ```shell # Create opentelemetry.properties export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) @@ -80,7 +82,7 @@ otel.javaagent.debug=false EOF ``` -### Running +## Running ```shell docker run \ @@ -89,4 +91,4 @@ docker run \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ -i edc-dataplane-hashicorp-vault:latest -``` \ No newline at end of file +``` diff --git a/edc-extensions/business-partner-validation/README.md b/edc-extensions/business-partner-validation/README.md index d37041560..79a0d7fc3 100644 --- a/edc-extensions/business-partner-validation/README.md +++ b/edc-extensions/business-partner-validation/README.md @@ -30,11 +30,13 @@ must contain the Business Partner Number. ## Single BusinessPartnerNumber example The most simple BPN policy would allow the usage of certain data to a single Business Partner. An example `Policy` is -shown below. -In this example the `edctype` properties are added, so that this policy may even be sent to the Management API. +shown below. In this example the `edctype` properties are added, so that this policy may even be sent to the Management API. ```json { + "uid": "", + "prohibitions": [], + "obligations": [], "permissions": [ { "edctype": "dataspaceconnector:permission", @@ -64,6 +66,7 @@ In this example the `edctype` properties are added, so that this policy may even To define multiple BPN and allow multiple participants to use the data the `orconstraint` should be used. It will permit the constraints contained to be evaluated using the `OR` operator. + ```json { "permissions": [ @@ -113,13 +116,12 @@ It will permit the constraints contained to be evaluated using the `OR` operator } ``` -# Important: EDC Policies are input sensitive +## Important: EDC Policies are input sensitive Please be aware that the EDC ignores all Rules and Constraint it does not understand. This could cause your constrained policies to be public. ---- +### Example 1 for accidentially public -**Example 1 for accidentially public:** ```json { "uid": "1", @@ -152,9 +154,7 @@ Please be aware that the EDC ignores all Rules and Constraint it does not unders This policy is public available, even though the constraint is described correct. The reason is, that this extension only registeres the Policy.Action `USE` within the EDC. Any other Action Type will have the EDC ignore the corresponding permission, hence interpret the polics as public policy. ---- - -**Example 2 for accidentally public:** +### Example 2 for accidentially public ```json { diff --git a/edc-extensions/cx-oauth2/README.md b/edc-extensions/cx-oauth2/README.md index 0da6f1ced..479c783c7 100644 --- a/edc-extensions/cx-oauth2/README.md +++ b/edc-extensions/cx-oauth2/README.md @@ -12,17 +12,17 @@ The reason IDS did this is to prevent the IDS DAPS to know, which connectors tal ## Configuration -| Key | Description | Mandatory | Default | -|:----|:----|----|----| -| edc.oauth.token.url | Token URL of the DAPS | X | | -| edc.oauth.public.key.alias | Vault alias of the public key | X | | -| edc.oauth.client.id | DAPS client id of the connector | X | | -| edc.oauth.private.key.alias | Vault lias of the private key | X | | -| edc.oauth.token.expiration.seconds | | | 5 minutes | -| edc.oauth.validation.nbf.leeway | DAPS token request leeway | | 10 seconds | -| edc.oauth.provider.jwks.refresh | Time between refresh of the DAPS json web key set | | 5 minutes | -| edc.ids.endpoint.audience | The audience the connector requests from the DAPS. Should be the IDS URL of the connector, e.g. `http://plato-edc-controlplane:8282/api/v1/ids/data` | X | | -| edc.ids.validation.referringconnector | Adds checks to the DAPS token. Validation that the `referringConnector` equals the `issuerConnector` and the `securityProfile` of the token is equal to the profile of the IDS message | | false | +| Key | Description | Mandatory | Default | +|:--------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|------------| +| edc.oauth.token.url | Token URL of the DAPS | X | | +| edc.oauth.public.key.alias | Vault alias of the public key | X | | +| edc.oauth.client.id | DAPS client id of the connector | X | | +| edc.oauth.private.key.alias | Vault lias of the private key | X | | +| edc.oauth.token.expiration.seconds | | | 5 minutes | +| edc.oauth.validation.nbf.leeway | DAPS token request leeway | | 10 seconds | +| edc.oauth.provider.jwks.refresh | Time between refresh of the DAPS json web key set | | 5 minutes | +| edc.ids.endpoint.audience | The audience the connector requests from the DAPS. Should be the IDS URL of the connector, e.g. `http://plato-edc-controlplane:8282/api/v1/ids/data` | X | | +| edc.ids.validation.referringconnector | Adds checks to the DAPS token. Validation that the `referringConnector` equals the `issuerConnector` and the `securityProfile` of the token is equal to the profile of the IDS message | | false | ## Audience Validation @@ -30,4 +30,4 @@ Instead of the `idsc:IDS_CONNECTORS_ALL` the connector requests a specific audie When a connector receives a message, it will checks the token audience is equal to the configured value in `edc.ids.endpoint.audience`. -![sequence diagram](./diagrams/sequence.png) \ No newline at end of file +![sequence diagram](./diagrams/sequence.png) diff --git a/edc-extensions/data-encryption/README.md b/edc-extensions/data-encryption/README.md index 60e01245f..586dad775 100644 --- a/edc-extensions/data-encryption/README.md +++ b/edc-extensions/data-encryption/README.md @@ -2,7 +2,7 @@ The Eclipse Dataspace Connector encrypts sensitive information inside a token it sends to other applications (from possibly other companies). This extension implements the encryption of this data and should be used with secure keys and algorithms at all times. -## Algorithm Configuration +## Algorithm Configuration | Key | Description | Mandatory | Default | |:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|-----------|------------------| @@ -17,6 +17,7 @@ The Advanced Encryption Standard (AES) is the default encryption algorithm. For When using AES-GCM the key length must be ether 128-, 196- or 256bit. Keys must be stored stored Base64 encoded in the Vault, separated by a comma. It's possible to generate Keys using OpenSSL + ```bash # 128 Bit openssl rand -base64 16 @@ -30,13 +31,12 @@ openssl rand -base64 32 #### AES Configuration -| Key | Description | Mandatory | Default | -|:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|-----------|------------------| -| edc.data.encryption.keys.alias | Symmetric Keys stored in the Vault under the configured alias. | X | | -| edc.data.encryption.caching.enabled | Enable caching to request only keys from the vault after the cache expires. | | false | -| edc.data.encryption.caching.seconds | Duration in seconds until the cache expires. | | 3600 | - +| Key | Description | Mandatory | Default | +|:------------------------------------|:----------------------------------------------------------------------------|-----------|---------| +| edc.data.encryption.keys.alias | Symmetric Keys stored in the Vault under the configured alias. | X | | +| edc.data.encryption.caching.enabled | Enable caching to request only keys from the vault after the cache expires. | | false | +| edc.data.encryption.caching.seconds | Duration in seconds until the cache expires. | | 3600 | ### 2. NONE -This strategy does apply no encryption at all and should only be used for debugging purposes. Using NONE encryption may leak sensitive data to other connectors! \ No newline at end of file +This strategy does apply no encryption at all and should only be used for debugging purposes. Using NONE encryption may leak sensitive data to other connectors! diff --git a/edc-extensions/dataplane-selector-configuration/README.md b/edc-extensions/dataplane-selector-configuration/README.md index 7a65b8f48..d5f922732 100644 --- a/edc-extensions/dataplane-selector-configuration/README.md +++ b/edc-extensions/dataplane-selector-configuration/README.md @@ -7,16 +7,17 @@ plane will look for an instance with matching capabilities to transfer data. Per data plane instance the following settings must be configured. As `` any unique string is valid. -| Key | Description | Mandatory | Example | -|:--------------------------------------------------------|:--------------------------------------------------|-----------|-------------------------------------------------------------------| -| edc.dataplane.selector.````.url | URL to connect to the Data Plane Instance. | X | http://plato-edc-dataplane:9999/api/dataplane/control | -| edc.dataplane.selector.````.sourcetypes | Source Types in a comma separated List. | X | HttpData | -| edc.dataplane.selector.````.destinationtypes | Destination Types in a comma separated List. | X | HttpProxy | +| Key | Description | Mandatory | Example | +|:------------------------------------------------------------|:--------------------------------------------------|-----------|------------------------------------------------------------------| +| edc.dataplane.selector.````.url | URL to connect to the Data Plane Instance. | X | | +| edc.dataplane.selector.````.sourcetypes | Source Types in a comma separated List. | X | HttpData | +| edc.dataplane.selector.````.destinationtypes | Destination Types in a comma separated List. | X | HttpProxy | | edc.dataplane.selector.````.properties | Additional properties of the Data Plane Instance. | (X) | { "publicApiUrl": "http://plato-edc-dataplane:8185/api/public" } | The property `publicApiUrl` is mandatory for Data Plane Instances with destination type `HttpProxy`. -**Helm Example Configuration using environment variables** +### Helm Example Configuration using environment variables + ```yaml EDC_DATAPLANE_SELECTOR_PLATOPLANE_URL: http://plato-edc-dataplane:9999/api/dataplane/control EDC_DATAPLANE_SELECTOR_PLATOPLANE_SOURCETYPES : HttpData diff --git a/edc-extensions/hashicorp-vault/README.md b/edc-extensions/hashicorp-vault/README.md index 7f49a4662..c3964605b 100644 --- a/edc-extensions/hashicorp-vault/README.md +++ b/edc-extensions/hashicorp-vault/README.md @@ -2,7 +2,7 @@ --- -**Please note:**
+**Please note:** Using the HashiCorp vault it is possible to define multiple data entries per secret. Other vaults might allow only one entry per secret (e.g. Azure Key Vault). @@ -25,23 +25,25 @@ creating secrets the EDC should consume. ## Health Check -The HashiCorp Vault Extension is able to run health checks. A health check is successful when the vault is _initialized_, _active_ and _unsealed_. Successful health checks are logged with level _FINE_. Unsuccessful health checks will be logged +The HashiCorp Vault Extension is able to run health checks. A health check is successful when the vault is _initialized_, _active_ and _unsealed_. Successful health checks are logged with level _FINE_. Unsuccessful health checks will be logged with level _WARNING_. --- -**Health Checks in Catena-X** + +### Health Checks in Catena-X If your project uses the Catena-X HashiCorp Vault please set `edc.vault.hashicorp.health.check.standby.ok` to _true_. Otherwise the health check would fail if the Vault is in standby. -```bash - # Logs of successful check with standby vault - [2022-08-01 14:48:37] [FINE ] HashiCorp Vault HealthCheck successful. HashicorpVaultHealthResponsePayload(isInitialized=true, isSealed=false, isStandby=true, isPerformanceStandby=false, replicationPerformanceMode=disabled,replicationDrMode=disabled, serverTimeUtc=1659365317, version=1.9.2, clusterName=vault-cluster-4b193c26, clusterId=83fabd45-685d-7f8d-9495-18fab6f50d5e) + +```plain +# Logs of successful check with standby vault +[2022-08-01 14:48:37] [FINE ] HashiCorp Vault HealthCheck successful. HashicorpVaultHealthResponsePayload(isInitialized=true, isSealed=false, isStandby=true, isPerformanceStandby=false, replicationPerformanceMode=disabled,replicationDrMode=disabled, serverTimeUtc=1659365317, version=1.9.2, clusterName=vault-cluster-4b193c26, clusterId=83fabd45-685d-7f8d-9495-18fab6f50d5e) ``` --- ## Example: Create & Configure DAPS Key -1. Insert DAPS Key into HashiCorp Vault +### Insert DAPS Key into HashiCorp Vault ```bash cat << EOF | /bin/vault kv put secret/my-daps-key content=- @@ -76,10 +78,10 @@ cat << EOF | /bin/vault kv put secret/my-daps-key content=- EOF ``` -2. Configure Key in the EDC +### Configure Key in the EDC ```bash - EDC_OAUTH_PRIVATE_KEY_ALIAS: my-daps-key +EDC_OAUTH_PRIVATE_KEY_ALIAS: my-daps-key ``` or @@ -90,9 +92,7 @@ edc.oauth.private.key.alias=my-daps-key ## Example: Catena-X Argo CD Vault Configuration - -``` - +```properties ######### # Vault # ######### @@ -109,5 +109,4 @@ edc.vault.hashicorp.health.check.standby.ok=true # from UI: secret stored in https://vault.demo.catena-x.net/ui/vault/secrets//show/my-daps-key edc.oauth.private.key.alias=my-daps-key - -``` \ No newline at end of file +``` diff --git a/edc-extensions/postgresql-migration/README.md b/edc-extensions/postgresql-migration/README.md index d96c2af5e..73f94eb56 100644 --- a/edc-extensions/postgresql-migration/README.md +++ b/edc-extensions/postgresql-migration/README.md @@ -1,6 +1,6 @@ # Postgresql SQL Migration Extension -This extension applies SQL migrations to +This extension applies SQL migrations to * the asset-index * the contract-definition store diff --git a/edc-tests/cucumber/README.md b/edc-tests/cucumber/README.md index a9424a5b0..e8c1a8ab1 100644 --- a/edc-tests/cucumber/README.md +++ b/edc-tests/cucumber/README.md @@ -6,7 +6,7 @@ THIS MODULE IS DEPRECATED AND WILL NOT BE MAINTAINED ANYMORE. ./gradlew :edc-tests:test -Dcucumber=true ``` -# Test locally using Act Tool +## Test locally using Act Tool > "Think globally, [`act`](https://github.com/nektos/act) locally" @@ -14,5 +14,6 @@ THIS MODULE IS DEPRECATED AND WILL NOT BE MAINTAINED ANYMORE. act -j business-test ``` -# Run and debug Business-Tests local within IDE +## Run and debug Business-Tests local within IDE + Please refer to [run-local documentation in docs](../docs/development/Run-business-tests-local.md) diff --git a/edc-tests/cucumber/src/main/resources/deployment/helm/omejdn/README.md b/edc-tests/cucumber/src/main/resources/deployment/helm/omejdn/README.md index 2fe8128db..f85a94889 100644 --- a/edc-tests/cucumber/src/main/resources/deployment/helm/omejdn/README.md +++ b/edc-tests/cucumber/src/main/resources/deployment/helm/omejdn/README.md @@ -7,6 +7,7 @@ Two Eclipse Dataspace Connectors need to be registered at the same DAPS instance New connectors are configured in the omejdn _values.yaml_. In each Eclipse Dataspace Connector configure the following properties to use the DAPS. + ```properties edc.oauth.client.id= @@ -17,4 +18,4 @@ In each Eclipse Dataspace Connector configure the following properties to use th edc.oauth.public.key.alias= edc.oauth.provider.audience=idsc:IDS_CONNECTORS_ALL -``` \ No newline at end of file +``` From 36b25f3fe035f5eaab6fa2aface5e5ead553de84 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Tue, 21 Mar 2023 22:17:27 +0100 Subject: [PATCH 004/132] Lint new changes from develop branch --- CHANGELOG.md | 5 ++ SECURITY.md | 2 +- charts/edc-controlplane/README.md | 4 +- charts/edc-controlplane/README.md.gotmpl | 4 +- charts/edc-dataplane/README.md | 4 +- charts/edc-dataplane/README.md.gotmpl | 4 +- charts/tractusx-connector/README.md | 5 +- charts/tractusx-connector/README.md.gotmpl | 5 +- docs/development/Release.md | 4 +- docs/development/Run-business-tests-local.md | 32 +++++-- docs/development/coding-principles.md | 14 +-- .../2023-02-09-release-process/README.md | 10 +-- .../2023-02-27_testing/README.md | 8 +- .../2023-03-02_gradle_build/README.md | 2 +- docs/development/postman/README.md | 16 ++-- docs/development/scripts/daps_token/README.md | 27 +++--- docs/migration/Version_0.1.2_0.1.3.md | 13 +-- docs/migration/Version_0.1.x_0.3.x.md | 4 +- docs/release-notes/Version 0.1.3.md | 24 +++--- docs/release-notes/Version 0.1.5.md | 2 +- docs/samples/Local TXDC Setup.md | 2 +- docs/samples/Transfer Data.md | 43 +++++----- docs/samples/data-plane-http-oauth2.md | 2 +- .../control-plane-adapter/README.md | 86 +++++++++---------- .../observability-api-customization/README.md | 4 +- .../provision-additional-headers/README.md | 1 + .../helm/supporting-infrastructure/README.md | 10 +-- pr_etiquette.md | 20 ++--- styleguide.md | 3 +- 29 files changed, 195 insertions(+), 165 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3960f0c9c..443e9f410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added ### Changed + - Support unauthenticated access to the ObservabilityAPI (#126) ### Fixed @@ -20,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). ### Added + - Add contract id to data source http call (#732) - Support also support releases in ci pipeline - Introduce typed object for oauth2 provisioning @@ -40,6 +42,7 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - Feature: Sftp Provisioner and Client (#554) ### Changed + - Support horizontal edc scaling in cp adapter extension (#678) - Use upstream jackson version (#741) - Replace provision-oauth2 with data-plane-http-oauth2 @@ -61,12 +64,14 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - update description of supporting infrastructure deployment (#616) ### Fixed + - bugfix: Fix slow AES encryption (#746) - Fix typo in tractusx-connector values.yaml comment - Fix not working docu link in README.md - Fix typo in control-plane adapter README ### Dependency updates + - Bump EDC to 20220220 (#767) - Bump alpine (#749) - Bump alpine (#750) diff --git a/SECURITY.md b/SECURITY.md index eec5ca437..41745e204 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,4 +2,4 @@ ## Reporting a Vulnerability -Please report a found vulnerability here: \ No newline at end of file +Please report a found vulnerability here: diff --git a/charts/edc-controlplane/README.md b/charts/edc-controlplane/README.md index 34b49b4e9..2e2a0cf68 100644 --- a/charts/edc-controlplane/README.md +++ b/charts/edc-controlplane/README.md @@ -11,8 +11,8 @@ EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with res ## TL;DR ```shell -$ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -$ helm install my-release tractusx-edc/edc-controlplane --version 0.3.0 +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/edc-controlplane --version 0.3.0 ``` ## Values diff --git a/charts/edc-controlplane/README.md.gotmpl b/charts/edc-controlplane/README.md.gotmpl index 022804eea..aa70ec6fc 100644 --- a/charts/edc-controlplane/README.md.gotmpl +++ b/charts/edc-controlplane/README.md.gotmpl @@ -11,8 +11,8 @@ ## TL;DR ```shell -$ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -$ helm install my-release tractusx-edc/edc-controlplane --version {{ .Version }} +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/edc-controlplane --version {{ .Version }} ``` {{ template "chart.maintainersSection" . }} diff --git a/charts/edc-dataplane/README.md b/charts/edc-dataplane/README.md index 02a26f41d..934ff72c1 100644 --- a/charts/edc-dataplane/README.md +++ b/charts/edc-dataplane/README.md @@ -11,8 +11,8 @@ EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility o ## TL;DR ```shell -$ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -$ helm install my-release tractusx-edc/edc-dataplane --version 0.3.0 +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/edc-dataplane --version 0.3.0 ``` ## Values diff --git a/charts/edc-dataplane/README.md.gotmpl b/charts/edc-dataplane/README.md.gotmpl index 8411b344e..c94d26d50 100644 --- a/charts/edc-dataplane/README.md.gotmpl +++ b/charts/edc-dataplane/README.md.gotmpl @@ -11,8 +11,8 @@ ## TL;DR ```shell -$ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -$ helm install my-release tractusx-edc/edc-dataplane --version {{ .Version }} +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/edc-dataplane --version {{ .Version }} ``` {{ template "chart.maintainersSection" . }} diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index 0624381bf..ccd0cae09 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -5,9 +5,10 @@ A Helm chart for Tractus-X Eclipse Data Space Connector ## TL;DR + ```shell -$ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc -$ helm install tractusx-connector catenax-ng-product-edc/tractusx-connector --version 0.3.0 +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/tractusx-connector --version 0.3.0 ``` ## Values diff --git a/charts/tractusx-connector/README.md.gotmpl b/charts/tractusx-connector/README.md.gotmpl index 47ef15755..b1671f5a2 100644 --- a/charts/tractusx-connector/README.md.gotmpl +++ b/charts/tractusx-connector/README.md.gotmpl @@ -9,9 +9,10 @@ {{ template "chart.homepageLine" . }} ## TL;DR + ```shell -$ helm repo add catenax-ng-product-edc https://catenax-ng.github.io/product-edc -$ helm install tractusx-connector catenax-ng-product-edc/tractusx-connector --version {{ .Version }} +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/tractusx-connector --version {{ .Version }} ``` {{ template "chart.maintainersSection" . }} diff --git a/docs/development/Release.md b/docs/development/Release.md index ded1e4a8b..3992c0a1d 100644 --- a/docs/development/Release.md +++ b/docs/development/Release.md @@ -9,7 +9,7 @@ ### 1. Setup Eclipse Dash License Tool locally For instructions on how to download the Eclipse Dash Tool executable, refer to the -project's [GitHub page](https://github.com/eclipse/dash-licenses#get-it). +project's [GitHub page](https://github.com/eclipse/dash-licenses#get-it). ### 2. Generate DEPENDENCIES file @@ -27,7 +27,7 @@ First, the dependencies of this module are calculated with gradle and passed to gradle allDependencies | grep -Poh "(?<=\s)[\w.-]+:[\w.-]+:[^:\s]+" | sort | uniq | java -jar /path/org.eclipse.dash.licenses-0.0.1-SNAPSHOT.jar - -summary DEPENDENCIES ``` -_Note: on some machines (e.g. macOS) [the ack tool](https://beyondgrep.com/install/) should be used instead of `grep`._ +_Note: on some machines (e.g. macOS) [the ack tool](https://beyondgrep.com/install/) should be used instead of `grep`._ ### 3. Resolve restricted Dependencies diff --git a/docs/development/Run-business-tests-local.md b/docs/development/Run-business-tests-local.md index 8a87ace24..9d66f7bbc 100644 --- a/docs/development/Run-business-tests-local.md +++ b/docs/development/Run-business-tests-local.md @@ -1,27 +1,33 @@ # Run and debug Business-Tests local within IDE -**Prerequisites:** + +Prerequisites: + - You need a local kubernetes cluster to install the services (Docker Desktop is recommended). - You need kubectl and helm command line tools installed. -### 1. Build all modules with maven and produce docker images +## 1. Build all modules with maven and produce docker images ```shell ./gradlew dockerize ``` -### 2. Install the all-in-one supporting infrastructure environment (Daps, Vault, PostgreSql, Minio, Backend-Service) +## 2. Install the all-in-one supporting infrastructure environment (Daps, Vault, PostgreSql, Minio, Backend-Service) + ```shel helm install infrastructure edc-tests/src/main/resources/deployment/helm/supporting-infrastructure -n business-tests --create-namespace ``` To access the PostgreSql databases you could use following kubectl port forwardings: + ```shell kubectl port-forward plato-postgresql-0 -n business-tests 5555:5432 kubectl port-forward sokrates-postgresql-0 -n business-tests 6666:5432 ``` + Please use the same ports later for your environment variables. -### 3. Install Plato as provider EDC +## 3. Install Plato as provider EDC + ```shell helm install plato charts/tractusx-connector -n business-tests --create-namespace \ --set fullnameOverride=plato \ @@ -56,7 +62,8 @@ helm install plato charts/tractusx-connector -n business-tests --create-namespac --wait-for-jobs --timeout=120s ``` -### 4. Install Socrates as consumer EDC +## 4. Install Socrates as consumer EDC + ```shell helm install sokrates charts/tractusx-connector -n business-tests --create-namespace \ --set fullnameOverride=sokrates \ @@ -91,9 +98,11 @@ helm install sokrates charts/tractusx-connector -n business-tests --create-names --wait-for-jobs --timeout=120s ``` -### 5. Set environment variables and run configuration in IDE +## 5. Set environment variables and run configuration in IDE + You can create a run configuration in IntelliJ like bellow screenshot and copy/paste the whole set of environments variables if you use ";" after each line. -![](run-config.png) + +![Example run config](run-config.png) ```shell PLATO_BACKEND_SERVICE_BACKEND_API_URL=http://localhost:; @@ -122,18 +131,23 @@ EDC_AWS_ENDPOINT_OVERRIDE=http://localhost:32000 The services are using NodePort to expose the endpoints therefore the ports are not fix and needs to be determined after each deployment. To determine the current ports you can use the following kubectl command: + ```shell kubectl get svc -n business-tests -o go-template='{{range .items}}{{ $save := . }}{{range.spec.ports}}{{if .nodePort}}{{$save.metadata.namespace}}{{"/"}}{{$save.metadata.name}}{{" - "}}{{.name}}{{": "}}{{.nodePort}}{{"("}}{{.port}}{{")"}}{{"\n"}}{{end}}{{end}}{{end}}' ``` + This will return all NodePorts which are available in business-tests namespace where you can pick the ports to use in your environment variables. Now you are able to run it in IDE either as normal "Run" mode or in "Debug" mode where you can debug the business-tests by setting debugging points. -### 6. Update your components +## 6. Update your components + Once everything is installed you just need to update your services when you have a new image. + ```shell helm upgrade plato charts/tractusx-connector --recreate-pods helm upgrade sokrates charts/tractusx-connector --recreate-pods ``` -### 7. Tips +## 7. Tips + If you use the kubernetes within Docker Desktop you have direct access to the images which you have created with Docker Desktop they are using the same docker daemon. So you don't need to transfer it in your k8s cluster. diff --git a/docs/development/coding-principles.md b/docs/development/coding-principles.md index f45c11b19..624186c46 100644 --- a/docs/development/coding-principles.md +++ b/docs/development/coding-principles.md @@ -73,15 +73,15 @@ - inheriting from an object that fulfills any of the above. In this case use derived builders as well. 2. Although serializability is not the reason we use the builder pattern, it is a strong indication that a builder should be used. -2. Builders should be named just `Builder` and be static nested classes. -3. Create a `public static Builder newInstance(){...}` method to instantiate the builder -4. Builders have non-public constructors -5. Use single-field builders: a `Builder` instantiates the object it builds in its constructor, and sets the properties +3. Builders should be named just `Builder` and be static nested classes. +4. Create a `public static Builder newInstance(){...}` method to instantiate the builder +5. Builders have non-public constructors +6. Use single-field builders: a `Builder` instantiates the object it builds in its constructor, and sets the properties in its builder methods. The `build()` method then only performs verification (optional) and returns the instance. -6. Use `private` constructors for the objects that the builder builds. -7. If there is a builder for an object, use it to deserialize an object, i.e. put Jackson annotations such +7. Use `private` constructors for the objects that the builder builds. +8. If there is a builder for an object, use it to deserialize an object, i.e. put Jackson annotations such as `JsonCreator` and `@JsonBuilder` on builders. -8. Note that the motivation behind use of builders is not for immutability (although that may be good in certain +9. Note that the motivation behind use of builders is not for immutability (although that may be good in certain circumstances). Rather, it is to make code less error-prone and simpler given the lack of named arguments and optional parameters in Java. diff --git a/docs/development/decision-records/2023-02-09-release-process/README.md b/docs/development/decision-records/2023-02-09-release-process/README.md index 0dffcd341..c67521eb6 100644 --- a/docs/development/decision-records/2023-02-09-release-process/README.md +++ b/docs/development/decision-records/2023-02-09-release-process/README.md @@ -21,7 +21,7 @@ from breaking changes, such as Java SPIs, APIs and changes in service contracts. Up until now, the only way out was cherry-picking, which is extremely cumbersome and error-prone, and requires a parallel build pipeline to publish the cherry-picked artifacts of EDC (and potentially others). With the approach -presented here, cherry-picking is still an option, but there are easier alternatives to it. +presented here, cherry-picking is still an option, but there are easier alternatives to it. Every release version published by tractusx-edc must be reproducible at any time. @@ -47,12 +47,12 @@ created on March 27th 2023, the most recent nightly would be `0.0.1-20230326`. _Updating Gradle files or Maven POMs, creating branches and tags in Git should be automated through GitHub Actions as part of the release process. For reference_: -- Modifying and committing files: https://github.com/orgs/community/discussions/26842#discussioncomment-3253612 -- Creating branches: https://github.com/marketplace/actions/create-branch +- Modifying and committing files: +- Creating branches: - Creating tags using GitHub's - API: https://github.com/eclipse-edc/Connector/blob/b24a5cacbc9fcabdfd8020d779399b3e56856661/.github/workflows/release-edc.yml#L21 ( + API: ( example) -- Create GitHub Release: https://github.com/eclipse-edc/Connector/blob/b24a5cacbc9fcabdfd8020d779399b3e56856661/.github/workflows/release-edc.yml#L56 (example) +- Create GitHub Release: (example) Once a release is created, the EDC upstream version must not change anymore, unless there is good reason to do so, for example, a defect, that needs to be fixed upstream. At that point a decision can also be made to employ a cherry-pick model, in case the diff --git a/docs/development/decision-records/2023-02-27_testing/README.md b/docs/development/decision-records/2023-02-27_testing/README.md index fa4b803e1..c4619eb8b 100644 --- a/docs/development/decision-records/2023-02-27_testing/README.md +++ b/docs/development/decision-records/2023-02-27_testing/README.md @@ -13,7 +13,7 @@ Henceforth, testing shall be done in accordance with the herein outlined rules a ## Rationale -Past experiences with product-edc's testing setup has shown that it is time- and resource-consuming, which also makes it unreliable at times. +Past experiences with product-edc's testing setup has shown that it is time- and resource-consuming, which also makes it unreliable at times. Furthermore, a finer-grained test classification such as the one outlined in this document is currently neither present nor documented. ### Definitions and distinction @@ -51,7 +51,7 @@ EDC provides a way to launch (multiple) embedded connector runtimes from within External systems such as databases or identity providers should be setup "out-of-band" of the test, using a script or the CI pipeline's declarative syntax (e.g. GitHub Actions' `services` feature). If possible, we should employ external systems in a self-contained way, e.g. using docker containers, because that increases portability and decreases the potential for conflict, e.g. in always-on databases. -### DO: +### DO - use integration tests sparingly and only when unit tests are not practical - deploy the external system as service directly in the workflow or @@ -63,7 +63,7 @@ External systems such as databases or identity providers should be setup "out-of system does not get destroyed after the test. - use the class annotations provided by EDC to categorize and configure test execution -### DO NOT: +### DO NOT - try to cover everything with integration tests. It's typically a code smell if there are no corresponding unit tests for an integration test. @@ -78,7 +78,7 @@ External systems such as databases or identity providers should be setup "out-of This section explains _at which point in time_ we should execute which test. This is intended to minimize the impact on overall test execution time on CI, while still maintaining sufficient coverage. | Test type | When to run | Remarks | -| ---------------------- | ----------------------------------------------------------------------------------- | ------- | +|------------------------|-------------------------------------------------------------------------------------|---------| | Unit test | when running tests locally, without any parameters, on every commit on every branch | | | Integration test | on every commit on every branch | | | System/End-To-End test | on pull request branches except when marked as `draft` | | diff --git a/docs/development/decision-records/2023-03-02_gradle_build/README.md b/docs/development/decision-records/2023-03-02_gradle_build/README.md index 0f8e7b327..4012599c1 100644 --- a/docs/development/decision-records/2023-03-02_gradle_build/README.md +++ b/docs/development/decision-records/2023-03-02_gradle_build/README.md @@ -43,6 +43,6 @@ parallelization resulting in faster and more responsive builds. ## Further consideration -Planned improvements regarding the testing procedure (PR https://github.com/catenax-ng/product-edc/pull/781) will also greatly benefit from the EDC build tools such +Planned improvements regarding the testing procedure (PR ) will also greatly benefit from the EDC build tools such as JUnit tags and conditional evaluation of the tagged tests. Much of EDC's testing framework is based on Gradle and can be seamlessly integrated in product-edc. diff --git a/docs/development/postman/README.md b/docs/development/postman/README.md index a6f5005b9..f4a7d2b24 100644 --- a/docs/development/postman/README.md +++ b/docs/development/postman/README.md @@ -8,20 +8,22 @@ The Postman app can be used to send and receive EDC messages. -### Install/Download Postman -please visit https://www.postman.com/downloads/ +### Install/Download Postman -### Import Postman collection? -please visit https://learning.postman.com/docs/getting-started/importing-and-exporting-data/ +please visit + +### Import Postman collection + +please visit ## Collection -The postman collection contains the most common API calls. Please note hat the +The postman collection contains the most common API calls. Please note that the - Policy & Negotiation calls come in pairs for the different kinds of policies -- The 'Data' call only works when using the All-In-One Deployment of this repository +- the 'Data' call only works when using the All-In-One Deployment of this repository ![screenshot](./images/screenshot.png) [postman-shield]: https://img.shields.io/badge/Postman-URL-orange -[postman-url]: https://www.postman.com \ No newline at end of file +[postman-url]: https://www.postman.com diff --git a/docs/development/scripts/daps_token/README.md b/docs/development/scripts/daps_token/README.md index aaeb49253..cbc7475ff 100644 --- a/docs/development/scripts/daps_token/README.md +++ b/docs/development/scripts/daps_token/README.md @@ -6,17 +6,22 @@ Script to request an IDS token from the DAPS. 1. Copy your DAPS private key into `key.pem` 2. Edit in the script the following variables - - `token_url` - - `client_id` - - `resource` + - `token_url` + - `client_id` + - `resource` 3. Run script -```bash -./daps_auth_sh -``` + ```bash + ./daps_auth_sh + ``` -4. Take the `access_token` from the output in use it in IDS messages -Script output: -```json -{"access_token":"eyJ0eXAiOiJhdCtqd3QiLCJraWQiOiI3MDM2MzAwNzVkYTM2N2IxYmZiYjRjY2Q0N2M1Y2ViMGQ5ZjM1MmRmYWU2MzJkMzYxMGMxNzNmMTM1NDI0NmM5IiwiYWxnIjoiUlMyNTYifQ.eyJzY29wZSI6Imlkc2M6SURTX0NPTk5FQ1RPUl9BVFRSSUJVVEVTX0FMTCIsImF1ZCI6WyJodHRwczovL3Blbi10ZXN0LXBsYXRvLXR4ZGMuaW50LmRlbW8uY2F0ZW5hLXgubmV0L2FwaS92MS9pZHMvZGF0YSJdLCJpc3MiOiJodHRwOi8vaWRzLWRhcHM6NDU2Ny8iLCJzdWIiOiI5OTo4MzpBNzoxNzo4NjpGRjo5ODo5MzpDRTpBMDpERDpBMTpGMTozNjpGQTpGNjowRjo3NTowQToyMzprZXlpZDo5OTo4MzpBNzoxNzo4NjpGRjo5ODo5MzpDRTpBMDpERDpBMTpGMTozNjpGQTpGNjowRjo3NTowQToyMyIsIm5iZiI6MTY3ODMxMDE0OSwiaWF0IjoxNjc4MzEwMTQ5LCJqdGkiOiJkZmY5Y2FmOS05NDZiLTQ1YmMtOWY4My0yYmJkMDI4NTlmYWMiLCJleHAiOjE2NzgzMTM3NDksImNsaWVudF9pZCI6Ijk5OjgzOkE3OjE3Ojg2OkZGOjk4OjkzOkNFOkEwOkREOkExOkYxOjM2OkZBOkY2OjBGOjc1OjBBOjIzOmtleWlkOjk5OjgzOkE3OjE3Ojg2OkZGOjk4OjkzOkNFOkEwOkREOkExOkYxOjM2OkZBOkY2OjBGOjc1OjBBOjIzIiwicmVmZXJyaW5nQ29ubmVjdG9yIjoiaHR0cDovL3BsYXRvLWNvbnRyb2xwbGFuZS9CUE5QTEFUTyJ9.JQqt9gCpaG7rLztO5-pJa7HIybVjKog9v0CFXHoVJZgdxMc5nTKZnuwBVHC1PXuWrBiyPxPoNg0TsfRg9DqF8rFD5noarxOJ1S84BF7AUUi3phQzBF26lsmNmOW_gdNBC-8xw1WMo5hRHH56cB64_x4V8T4VwFlSYYrmA5ge_EiPCW_KWF9sNguXBKs8uTbLB3lvTELGTjmZI93tVR-vYuYzW2jxH1PJNW29KJRQcM0D1AiveMs3_ThRjheEvugyh9QIY1RwPXMgYQpSTvoumNuFFTnpR21ueWfSUtU-4Qu9suNTkcaFihvEObXVrhyMja-HjhQaC8i0XsAgY0tT1A","expires_in":3600,"token_type":"bearer","scope":"idsc:IDS_CONNECTOR_ATTRIBUTES_ALL"}% -``` +4. Take the `access_token` from the output in use it in IDS messages. The output of the script looks like this: + + ```json + { + "access_token": "eyJ0eXAiOiJhdCtqd3QiLCJraWQiOiI3MDM2MzAwNzVkYTM2N2IxYmZiYjRjY2Q0N2M1Y2ViMGQ5ZjM1MmRmYWU2MzJkMzYxMGMxNzNmMTM1NDI0NmM5IiwiYWxnIjoiUlMyNTYifQ.eyJzY29wZSI6Imlkc2M6SURTX0NPTk5FQ1RPUl9BVFRSSUJVVEVTX0FMTCIsImF1ZCI6WyJodHRwczovL3Blbi10ZXN0LXBsYXRvLXR4ZGMuaW50LmRlbW8uY2F0ZW5hLXgubmV0L2FwaS92MS9pZHMvZGF0YSJdLCJpc3MiOiJodHRwOi8vaWRzLWRhcHM6NDU2Ny8iLCJzdWIiOiI5OTo4MzpBNzoxNzo4NjpGRjo5ODo5MzpDRTpBMDpERDpBMTpGMTozNjpGQTpGNjowRjo3NTowQToyMzprZXlpZDo5OTo4MzpBNzoxNzo4NjpGRjo5ODo5MzpDRTpBMDpERDpBMTpGMTozNjpGQTpGNjowRjo3NTowQToyMyIsIm5iZiI6MTY3ODMxMDE0OSwiaWF0IjoxNjc4MzEwMTQ5LCJqdGkiOiJkZmY5Y2FmOS05NDZiLTQ1YmMtOWY4My0yYmJkMDI4NTlmYWMiLCJleHAiOjE2NzgzMTM3NDksImNsaWVudF9pZCI6Ijk5OjgzOkE3OjE3Ojg2OkZGOjk4OjkzOkNFOkEwOkREOkExOkYxOjM2OkZBOkY2OjBGOjc1OjBBOjIzOmtleWlkOjk5OjgzOkE3OjE3Ojg2OkZGOjk4OjkzOkNFOkEwOkREOkExOkYxOjM2OkZBOkY2OjBGOjc1OjBBOjIzIiwicmVmZXJyaW5nQ29ubmVjdG9yIjoiaHR0cDovL3BsYXRvLWNvbnRyb2xwbGFuZS9CUE5QTEFUTyJ9.JQqt9gCpaG7rLztO5-pJa7HIybVjKog9v0CFXHoVJZgdxMc5nTKZnuwBVHC1PXuWrBiyPxPoNg0TsfRg9DqF8rFD5noarxOJ1S84BF7AUUi3phQzBF26lsmNmOW_gdNBC-8xw1WMo5hRHH56cB64_x4V8T4VwFlSYYrmA5ge_EiPCW_KWF9sNguXBKs8uTbLB3lvTELGTjmZI93tVR-vYuYzW2jxH1PJNW29KJRQcM0D1AiveMs3_ThRjheEvugyh9QIY1RwPXMgYQpSTvoumNuFFTnpR21ueWfSUtU-4Qu9suNTkcaFihvEObXVrhyMja-HjhQaC8i0XsAgY0tT1A", + "expires_in": 3600, + "token_type": "bearer", + "scope": "idsc:IDS_CONNECTOR_ATTRIBUTES_ALL" + } + ``` diff --git a/docs/migration/Version_0.1.2_0.1.3.md b/docs/migration/Version_0.1.2_0.1.3.md index 55c9c4570..787b04bfe 100644 --- a/docs/migration/Version_0.1.2_0.1.3.md +++ b/docs/migration/Version_0.1.2_0.1.3.md @@ -6,15 +6,18 @@ This document contains a list of breaking changes that are introduced in version As the images now use the official OAuth2 Extension, the audience settings need to the updated. -**Add the following settings** +Add the following settings: + - EDC_OAUTH_PROVIDER_AUDIENCE - EDC_OAUTH_ENDPOINT_AUDIENCE -**Remove the following setting** +Remove the following setting: + - EDC_IDS_ENDPOINT_AUDIENCE -Example -``` +Example: + +```yaml EDC_OAUTH_PROVIDER_AUDIENCE: idsc:IDS_CONNECTORS_ALL EDC_OAUTH_ENDPOINT_AUDIENCE: http://plato-edc-controlplane:8282/api/v1/ids/data -``` \ No newline at end of file +``` diff --git a/docs/migration/Version_0.1.x_0.3.x.md b/docs/migration/Version_0.1.x_0.3.x.md index cbfd17e83..f35d3aa5e 100644 --- a/docs/migration/Version_0.1.x_0.3.x.md +++ b/docs/migration/Version_0.1.x_0.3.x.md @@ -6,7 +6,7 @@ ## Management API changes -details at the [official documentation on swaggerhub](https://app.swaggerhub.com/apis/eclipse-edc-bot/management-api/0.0.1-SNAPSHOT) +Details at the [official documentation on swaggerhub](https://app.swaggerhub.com/apis/eclipse-edc-bot/management-api/0.0.1-SNAPSHOT) - Management API for creating resources (assets, policydefinitions, contractdefinitions, ...) will return a body containing the id of the created resource - Added a `POST /request` for every management endpoint (assets, policydefinitions, ...) to query all the resources. The existent `GET /` have been deprecated @@ -27,7 +27,7 @@ details at the [official documentation on swaggerhub](https://app.swaggerhub.com - renamed `edc.receiver.http.endpoint` to `edc.receiver.http.dynamic.endpoint` - renamed `edc.oauth.public.key.alias` setting to `edc.oauth.certificate.alias` -## Other changes: +## Other changes - Supported `/public` data plane endpoint without trailing slash, that can be eventually removed from the configuration - packages name changed from `org.eclipse.dataspaceconnector` to `org.eclipse.edc` diff --git a/docs/release-notes/Version 0.1.3.md b/docs/release-notes/Version 0.1.3.md index 2ee32093f..c44d60cdb 100644 --- a/docs/release-notes/Version 0.1.3.md +++ b/docs/release-notes/Version 0.1.3.md @@ -10,23 +10,23 @@ - Business Partner Extension - HashiCorp Vault Extension - OAuth2 Extension -3. Bug Fixes +3. Bug Fixes - S3 Data Transfer -# 1. Container Images +## 1. Container Images -## 1.1 New Image: HashiCorp Vault & In Memory Store +### 1.1 New Image: HashiCorp Vault & In Memory Store The EDC now releases a fourth image with a combination of HashiCorp Vault and In Memory Store extensions. -# 2. Extensions +## 2. Extensions -## 2.1 Business Partner Extension +### 2.1 Business Partner Extension **Removed support for Constraint with multiple BPNs** The possibility to use multiple Business Partner Numbers inside of a single constraint has been removed. It looks like this was only possible due to a missing feature and may lead to unexpected side -effects (https://github.com/eclipse-dataspaceconnector/DataSpaceConnector/issues/2026) +effects () Hence, this kind of policy is no longer supported! @@ -65,18 +65,18 @@ Hence, this kind of policy is no longer supported! The BPN extension will now always decline BPN policies with 'IN' operators, when asked by the EDC to enforce it. -## 2.2 HashiCorp Vault Extension +### 2.2 HashiCorp Vault Extension It is now possible to arrange HashiCorp Vault secrets in sub-directories. For example by storing the DAPS secrets in their own `/daps` directory: -``` +```yaml EDC_OAUTH_PRIVATE_KEY_ALIAS: daps/my-plato-daps-key EDC_OAUTH_PUBLIC_KEY_ALIAS: daps/my-plato-daps-crt ``` -## 2.3 OAuth2 Extension +### 2.3 OAuth2 Extension The EDC Oauth2 Extension has now the possibility to add the audience to the claim. As the official OAuth2 Extension was added to the control plane again most of the functionality of the CX Oauth2 Extension was removed. @@ -84,8 +84,8 @@ added to the control plane again most of the functionality of the CX Oauth2 Exte > **Breaking Change** The official OAuth2 Extension uses different settings then the EDC OAuth Extension. Please > consolidate the [Migration Documentation](../migration/Version_0.1.2_0.1.3.md). -# 3. Bug Fixes +## 3. Bug Fixes -## 3.1 S3 Data Transfer +### 3.1 S3 Data Transfer -Version 0.1.2 had some issues with the S3 data transfer. This version fixes them. \ No newline at end of file +Version 0.1.2 had some issues with the S3 data transfer. This version fixes them. diff --git a/docs/release-notes/Version 0.1.5.md b/docs/release-notes/Version 0.1.5.md index 37bac446f..242be5494 100644 --- a/docs/release-notes/Version 0.1.5.md +++ b/docs/release-notes/Version 0.1.5.md @@ -22,4 +22,4 @@ catalog pagination. [GitHub issue](https://github.com/eclipse-edc/Connector/issu ### 2.2 Data Encryption Extension The encryption of the `EndpointDataReference` took up to 3 minutes unter certain circumstances. -This was fixed by using a not blocking algorithm and setting the Java CMD flag `java.security.egd` correctly. \ No newline at end of file +This was fixed by using a not blocking algorithm and setting the Java CMD flag `java.security.egd` correctly. diff --git a/docs/samples/Local TXDC Setup.md b/docs/samples/Local TXDC Setup.md index 8117dc05d..ad2a0f6bc 100644 --- a/docs/samples/Local TXDC Setup.md +++ b/docs/samples/Local TXDC Setup.md @@ -121,4 +121,4 @@ helm uninstall --namespace cx plato helm uninstall --namespace cx sokrates ``` -> To try out the local setup, have a look at the [Transfer Example Documentation](Transfer%20Data.md) \ No newline at end of file +> To try out the local setup, have a look at the [Transfer Example Documentation](Transfer%20Data.md) diff --git a/docs/samples/Transfer Data.md b/docs/samples/Transfer Data.md index f07f685f9..d68d87561 100644 --- a/docs/samples/Transfer Data.md +++ b/docs/samples/Transfer Data.md @@ -6,18 +6,18 @@ For this transfer connector **Bob** will act as data provider, and connector **A consumer. But the roles could be inverse as well. > Please note: Before running the examples the corresponding environment variables must be set. -> How such an environment can be setup locally is documented in [chapter 0](#0--optional--local-setup). +> How such an environment can be setup locally is documented in [chapter 1](#1--optional--local-setup). -**Contents** +## Table of Content -0. [(optional) Local Setup](#0--optional--local-setup) -1. [Setup Data Offer](#1-setup-data-offer) -2. [Request Contract Offers](#2-request-contract-offer-catalog) -3. [Negotiate Contract](#3-negotiate-contract) -4. [Transfer Data](#4-transfer-data) -5. [Verify Data Transfer](#5-verify-data-transfer) +1. [(optional) Local Setup](#1--optional--local-setup) +2. [Setup Data Offer](#2-setup-data-offer) +3. [Request Contract Offers](#3-request-contract-offer-catalog) +4. [Negotiate Contract](#4-negotiate-contract) +5. [Transfer Data](#5-transfer-data) +6. [Verify Data Transfer](#6-verify-data-transfer) -## 0. (optional) Local Setup +## 1. (optional) Local Setup To create a local setup with two connectors have a look at the [Local TXDC Setup Documentation](Local%20TXDC%20Setup.md). @@ -33,7 +33,7 @@ minkube service list Minikube will then print out something like this: -```shell +```plain |-------------|-----------------------|-----------------|---------------------------| | NAMESPACE | NAME | TARGET PORT | URL | |-------------|-----------------------|-----------------|---------------------------| @@ -83,7 +83,7 @@ kubectl describe service -n cx sokrates-controlplane Kubernetes will then print out something like this. -```shell +```plain Name: plato-controlplane Namespace: cx Labels: app.kubernetes.io/component=edc-controlplane @@ -138,6 +138,7 @@ required. Where to get the IP may vary depending on how Kubernetes is deployed. ### Set Environment Variables, used by this example Environment Variables, containing a URL, used by this example are + - BOB_DATAMGMT_URL - ALICE_DATAMGMT_URL - BOB_IDS_URL @@ -153,7 +154,7 @@ Let's assume we will use Sokrates as Bob, and Plato as Alice. **ALICE_BACKEND_URL** must the Node URL. In this local setup it would be `http://192.168.49.2:30193` -## 1. Setup Data Offer +## 2. Setup Data Offer Set up a data offer in **Bob**, so that **Alice** has something to consume. @@ -162,8 +163,6 @@ official open source documentation ([link](https://github.com/eclipse-edc/Connec ![Sequence 1](diagrams/transfer_sequence_1.png) -**Run** - The following commands will create an Asset, a Policy and a Contract Definition. For simplicity `https://jsonplaceholder.typicode.com/todos/1` is used as data source of the asset, but could be any other API, that is reachable from the Provider Data Plane. @@ -229,7 +228,7 @@ curl -X POST "${BOB_DATAMGMT_URL}/data/contractdefinitions" \ -s -o /dev/null -w 'Response Code: %{http_code}\n' ``` -## 2. Request Contract Offer Catalog +## 3. Request Contract Offer Catalog In this step Alice gets told to request contract offers from another connector (in this case Bob). Alice will then request the catalog over IDS messaging. @@ -239,7 +238,7 @@ connectors, that intent to send messages to each other, have the same DAPS insta ![Sequence 1](diagrams/transfer_sequence_2.png) -**Run** +Run: ```bash curl -G -X GET "${ALICE_DATAMGMT_URL}/data/catalog" \ @@ -249,7 +248,7 @@ curl -G -X GET "${ALICE_DATAMGMT_URL}/data/catalog" \ -s | jq ``` -## 3. Negotiate Contract +## 4. Negotiate Contract Initiate a contract negotiation for the asset (from step 1). Part of the negotiation payload is the contract offer (received in step 2). @@ -262,7 +261,7 @@ and checking whether the `contractAgreementId` is set. This might take a few sec ![Sequence 1](diagrams/transfer_sequence_3.png) -**Run** +Run: ```bash export NEGOTIATION_ID=$( \ @@ -300,14 +299,14 @@ curl -X GET "${ALICE_DATAMGMT_URL}/data/contractnegotiations/${NEGOTIATION_ID}" -s | jq ``` -## 4. Transfer Data +## 5. Transfer Data Initiate a data transfer using the contract agreement from the negotiation (from step 3). Then wait until the state of the transfer process is `COMPLETED`. ![Sequence 1](diagrams/transfer_sequence_4.png) -**Run** +Run: ```bash export CONTRACT_AGREEMENT_ID=$( \ @@ -342,7 +341,7 @@ curl -X GET "${ALICE_DATAMGMT_URL}/data/transferprocess/${TRANSFER_ID}" \ -s | jq ``` -## 5. Verify Data Transfer +## 6. Verify Data Transfer After the transfer is complete the Backend Application has downloaded the data. The Backend Application stores the data locally. In this demo the transfer can be verified by executing a simple `cat` call in the Pod. @@ -355,7 +354,7 @@ curl -X GET "${ALICE_BACKEND_URL}/${TRANSFER_PROCESS_ID}" \ -s | jq ``` -# Delete All Data +## Delete All Data ```bash minikube kubectl -- delete pvc -n edc-all-in-one --all diff --git a/docs/samples/data-plane-http-oauth2.md b/docs/samples/data-plane-http-oauth2.md index f757b703a..63b99319f 100644 --- a/docs/samples/data-plane-http-oauth2.md +++ b/docs/samples/data-plane-http-oauth2.md @@ -4,4 +4,4 @@ The Data Plane HTTP OAuth2 extension permits the data-plane to fetch the data re with an OAuth2 authentication layer. For further documentation, please refer to the extension README: -https://github.com/eclipse-edc/Connector/tree/main/extensions/data-plane/data-plane-http-oauth2-core + diff --git a/edc-extensions/control-plane-adapter/README.md b/edc-extensions/control-plane-adapter/README.md index 7b6dfa31b..fe9d4787c 100644 --- a/edc-extensions/control-plane-adapter/README.md +++ b/edc-extensions/control-plane-adapter/README.md @@ -1,68 +1,69 @@ # Control Plane Adapter Extension -The goal of this extension is to simplify the process of retrieving data out of EDC. It returns "EndpointDataReference" object, hiding all the communication details for contract offers, contract negotiation process and retrieving DataReference from EDC control-plane. +The goal of this extension is to simplify the process of retrieving data out of EDC. It returns an `EndpointDataReference` object, hiding all the communication details for contract offers, contract negotiation process and retrieving `EndpointDataReference` from EDC controlplane. Additional requirements, that affects the architecture of the extension: + - can return data both in SYNC and ASYNC mode (currently only SYNC endpoint available) - can be persistent, so that process can be restored from the point where it was before application was stopped - scaling horizontally (when persistence is added to configuration) - can retry failed part of the process (no need to start the process from the beginning) -## Configuration: +## Configuration -| Key | Description | Mandatory | Default | -|:-------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------|---------| -| edc.cp.adapter.default.message.retry.number | Number of retries of a message, in case of an error, within the internal process of retrieving DataReference | no | 3 | -| edc.cp.adapter.default.sync.request.timeout | Timeout for synchronous request (in seconds), after witch 'timeout' error will be returned to the requesting client | no | 20 | -| edc.cp.adapter.messagebus.inmemory.thread.number | Number of threads running within the in-memory implementation of MessageBus _ _ | no | 10 | -| edc.cp.adapter.reuse.contract.agreement | Turn on/off reusing of existing contract agreements for the specific asset. Once the contract is agreed, the second request for the same asset will reuse the agreement (if exists) pulled from the EDC. | no | true | -| edc.cp.adapter.cache.catalog.expire.after | Number of seconds, after witch prevoiusly requested catalog will not be reused, and will be removed from catalog cache | no | 300 | -| edc.cp.adapter.catalog.request.limit | Maximum number of items taken from Catalog within single request. Requests are repeated until all offers of the query are retrieved | no | 100 | +| Key | Description | Mandatory | Default | +|:---------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|---------| +| `edc.cp.adapter.default.message.retry.number` | Number of retries of a message, in case of an error, within the internal process of retrieving DataReference | no | 3 | +| `edc.cp.adapter.default.sync.request.timeout` | Timeout for synchronous request (in seconds), after witch 'timeout' error will be returned to the requesting client | no | 20 | +| `edc.cp.adapter.messagebus.inmemory.thread.number` | Number of threads running within the in-memory implementation of MessageBus | no | 10 | +| `edc.cp.adapter.reuse.contract.agreement` | Turn on/off reusing of existing contract agreements for the specific asset. Once the contract is agreed, the second request for the same asset will reuse the agreement (if exists) pulled from the EDC. | no | true | +| `edc.cp.adapter.cache.catalog.expire.after` | Number of seconds, after witch previously requested catalog will not be reused, and will be removed from catalog cache | no | 300 | +| `edc.cp.adapter.catalog.request.limit` | Maximum number of items taken from Catalog within single request. Requests are repeated until all offers of the query are retrieved | no | 100 | By default, the extension works in "IN MEMORY" mode. This setup has some limitations: -+ It can work only within single EDC instance. If CP-adapter requests are handled by more than one EDC, data flow may be broken. -+ If the EDC instance is restarted, all running processes are lost. -To run CP-Adapter in "PERSISTENT" mode, You need to create a proper tables with [this](docs/schema.sql) script, and add the following configuration values to Your control-plane EDC properties file: +- It can work only within single EDC instance. If CP-adapter requests are handled by more than one EDC, data flow may be broken. +- If the EDC instance is restarted, all running processes are lost. -| Key | Description | -|-----------------------------------|-------------| -| edc.datasource.cpadapter.name | data source name | -| edc.datasource.cpadapter.url | data source url | -| edc.datasource.cpadapter.user | data source user | -| edc.datasource.cpadapter.password | data source password | +To run CP-Adapter in "PERSISTENT" mode, You need to create a proper tables with [this](docs/schema.sql) script, and add the following configuration values to your controlplane EDC properties file: +| Key | Description | +|-------------------------------------|----------------------| +| `edc.datasource.cpadapter.name` | data source name | +| `edc.datasource.cpadapter.url` | data source url | +| `edc.datasource.cpadapter.user` | data source user | +| `edc.datasource.cpadapter.password` | data source password | -## How to use it: -1. Client sends a GET request with two parameters: assetId and the url of the provider control-plane: +## How to use it - ``` +1. Client sends a GET request with two parameters: assetId and the url of the provider controlplane: + + ```plain /adapter/asset/sync/{assetId}?providerUrl={providerUrl} ``` The example ULR could be: - ``` + ```plain http://localhost:9193/api/v1/data/adapter/asset/sync/123?providerUrl=http://localhost:8182/api/v1/ids/data ``` - + Optional request parameters, that overwrite the settings for a single request: - | Name | Description | - |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--- | - | contractAgreementId | Defines the ID of existing contract agreement, that should be reused for retrieving the asset. If parameter is specified, but contract is not found, 404 error will be returned. | - | contractAgreementReuse | Similar to edc.cp.adapter.reuse.contract.agreement option allows to turn off reusing of existing contracts, but on a request level. Set the parameter value to 'false' and new contract agrement will be negotiated. | - | timeout | Similar to edc.cp.adapter.default.sync.request.timeout, defines the maximum time of the request. If data is not ready, time out error will be returned. | - - The controller is registered under the context alias of DataManagement API. The authentication depends on the DataManagement configuration. + | Name | Description | + |--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + | `contractAgreementId` | Defines the ID of existing contract agreement, that should be reused for retrieving the asset. If parameter is specified, but contract is not found, 404 error will be returned. | + | `contractAgreementReuse` | Similar to `edc.cp.adapter.reuse.contract.agreement` option allows to turn off reusing of existing contracts, but on a request level. Set the parameter value to 'false' and new contract agrement will be negotiated. | + | `timeout` | Similar to `edc.cp.adapter.default.sync.request.timeout`, defines the maximum time of the request. If data is not ready, time out error will be returned. | + + The controller is registered under the context alias of the Management API. The authentication depends on the configuration of the Management API. To find out more please visit: - [api-configuration](../../edc/extensions/control-plane/api/data-management/api-configuration/README.md) + - [Management API Documentation](https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/api/management-api) + - [Management API Configuration Extension](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/api/management-api-configuration) - [data-management](../../edc/extensions/control-plane/api/data-management/README.md) +2. `EndpointDataReference` object is returned. Example of the `EndpointDataReference` response: - -2. EndpointDataReference object is returned. Example of the EndpointDataReference response: ```json { "id": "ee8b758a-4b02-4cca-bb37-d0256b4638e7", @@ -75,18 +76,15 @@ To run CP-Adapter in "PERSISTENT" mode, You need to create a proper tables with } ``` -3. Client, using the DataReference, retrieves the Asset through data-plane. - - Example of the data-plane GET request, to retrieve Asset, with DataReference information: - - ``` +3. Client, using the `EndpointDataReference`, retrieves the Asset through dataplane. + + Example of the dataplane GET request, to retrieve Asset, with `EndpointDataReference` information: + + ```plain url: http://consumer-dataplane:9192/publicsubmodel?provider-connector-url=... {endpoint} header: Authorization:eyJhbGciOiJSUzI1NiJ9.eyJkYWQiOi... {authKey:authCode} ``` -### Internal design of the extension: +### Internal design of the extension ![diagram](src/main/resources/control-plane-adapter.jpg) - - - diff --git a/edc-extensions/observability-api-customization/README.md b/edc-extensions/observability-api-customization/README.md index ba57a40e6..920d76afc 100644 --- a/edc-extensions/observability-api-customization/README.md +++ b/edc-extensions/observability-api-customization/README.md @@ -9,7 +9,7 @@ API gets registered, and whether insecure (= unauthenticated) access is allowed. If no additional configuration is done, the Observability API is registered into the `"management"` context of EDC. That means the following configuration values **must be present** -``` +```properties web.http.management.port= web.http.management.path=/some/api/path ``` @@ -30,4 +30,4 @@ If the `tractusx.api.observability.allow-insecure=true` is set, then the Observa into the `observability` context, which is unsecured. > Disclaimer: allowing unsecured access to APIs is dangerous and a potential security risk! Using authenticated access -> to all APIs is highly recommended. Never expose unsecured APIs to the public! \ No newline at end of file +> to all APIs is highly recommended. Never expose unsecured APIs to the public! diff --git a/edc-extensions/provision-additional-headers/README.md b/edc-extensions/provision-additional-headers/README.md index 3d68602fa..1883f370f 100644 --- a/edc-extensions/provision-additional-headers/README.md +++ b/edc-extensions/provision-additional-headers/README.md @@ -6,4 +6,5 @@ in order to retrieve the data that will be given to the consumer. This gives for example the provider backend service the possibility to audit the data requests. The following headers are added to the `HttpDataAddress`: + - `Edc-Contract-Agreement-Id`: the id of the contract agreement diff --git a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/README.md b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/README.md index 3aae7191a..89cadb1aa 100644 --- a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/README.md +++ b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/README.md @@ -68,19 +68,19 @@ Follow these steps to get a fully functional EDC demo environment out of the box Install on your machine: - Minikube - - Documentation https://minikube.sigs.k8s.io/docs/start/ + - Documentation - Helm - - Documentation https://helm.sh/docs/intro/install/ + - Documentation ## Start Demo Environment -**Update Helm Dependencies** +Update Helm Dependencies: ```bash helm dependency update ``` -**Install Demo Chart** +Install Demo Chart: ```bash helm install tx-infrastructure --namespace tx --create-namespace . @@ -88,7 +88,7 @@ helm install tx-infrastructure --namespace tx --create-namespace . ## Stop Demo Environment -**Uninstall Demo Chart** +Uninstall Demo Chart: ```bash helm uninstall tx-infrastructure --namespace tx diff --git a/pr_etiquette.md b/pr_etiquette.md index 4b288e2d8..ce9ec73f8 100644 --- a/pr_etiquette.md +++ b/pr_etiquette.md @@ -33,10 +33,10 @@ Submitting pull requests in EDC should be done while adhering to a couple of sim to either accept the decision or withdraw your PR. - Be civil and objective. No foul language, insulting or otherwise abusive language will be tolerated. - The PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). - - The title must follow the format as `(): `. - `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test` are allowed for - the ``. - - The length must be kept under 80 characters. + - The title must follow the format as `(): `. + `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test` are allowed for + the ``. + - The length must be kept under 80 characters. ## As a reviewer @@ -48,12 +48,12 @@ Submitting pull requests in EDC should be done while adhering to a couple of sim - Don't argue basic principles (code style, architectural decisions, etc.) - Use the `suggestion` feature of GitHub for small/simple changes. - The following could serve you as a review checklist: - - no unnecessary dependencies in `build.gradle.kts` - - sensible unit tests, prefer unit tests over integration tests wherever possible (test runtime). Also check the - usage of test tags. - - code style - - simplicity and "uncluttered-ness" of the code - - overall focus of the PR + - no unnecessary dependencies in `build.gradle.kts` + - sensible unit tests, prefer unit tests over integration tests wherever possible (test runtime). Also check the + usage of test tags. + - code style + - simplicity and "uncluttered-ness" of the code + - overall focus of the PR - Don't just wave through any PR. Please take the time to look at them carefully. - Be civil and objective. No foul language, insulting or otherwise abusive language will be tolerated. The goal is to _encourage_ contributions. diff --git a/styleguide.md b/styleguide.md index 52835f811..e5183465b 100644 --- a/styleguide.md +++ b/styleguide.md @@ -51,7 +51,8 @@ If you absolutely want to make sure that no piece of ever-so-slightly misformatt advise you to use the [SaveActions plugin](https://plugins.jetbrains.com/plugin/7642-save-actions) for IntelliJ IDEA. It takes care that your code is always correctly formatted. Unfortunately SaveActions has no export feature, so please just copy this configuration: -![](resources/save_actions_scr.png) + +![SaveActions configuration](resources/save_actions_scr.png) ## [Optional] Generic `.editorConfig` From d032db8c33f3e8b60b54741f991b1754c66c0230 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Tue, 21 Mar 2023 22:41:58 +0100 Subject: [PATCH 005/132] Replace appearance of product-edc with tractusx-edc --- .github/workflows/business-tests.yaml | 12 ++++++------ CHANGELOG.md | 2 +- README.md | 2 +- .../templates/deployment-controlplane.yaml | 6 +++--- .../templates/deployment-dataplane.yaml | 2 +- docs/README.md | 2 +- .../decision-records/2023-02-27_testing/README.md | 4 ++-- .../2023-03-02_gradle_build/README.md | 8 ++++---- docs/development/postman/collection.json | 2 +- docs/migration/Version_0.0.x_0.1.x.md | 4 ++-- docs/release-notes/Version 0.1.2.md | 4 ++-- docs/samples/Transfer Data.md | 2 +- .../deployment/helm/omejdn/templates/deployment.yaml | 2 +- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index f55d3d6ba..b50a801d6 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -84,7 +84,7 @@ jobs: - role: control-plane extraMounts: - hostPath: ${PWD} - containerPath: /srv/product-edc + containerPath: /srv/tractusx-edc - hostPath: ${MAVEN_REPOSITORY} containerPath: /srv/m2-repository - hostPath: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs @@ -262,7 +262,7 @@ jobs: { "args": [ "-c", - "cd /product-edc && ./gradlew edc-tests:cucumber:test -Dcucumber=true" + "cd /tractusx-edc && ./gradlew edc-tests:cucumber:test -Dcucumber=true" ], "command": [ "/bin/sh" @@ -301,8 +301,8 @@ jobs: "name": "edc-tests-cucumber", "volumeMounts": [ { - "mountPath": "/product-edc", - "name": "product-edc" + "mountPath": "/tractusx-edc", + "name": "tractusx-edc" }, { "mountPath": "/root/.m2/repository", @@ -316,9 +316,9 @@ jobs: "volumes": [ { "hostPath": { - "path": "/srv/product-edc" + "path": "/srv/tractusx-edc" }, - "name": "product-edc" + "name": "tractusx-edc" }, { "hostPath": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 443e9f410..4674ba285 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -232,7 +232,7 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Changed -- Release Workflow now publishes Product EDC Extensions as Maven Artifacts +- Release Workflow now publishes EDC Extensions as Maven Artifacts ### Fixed diff --git a/README.md b/README.md index 0d9ef46e8..6f3fc8232 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Derivatives of the Data-Plane can be found here ### Build -Build Product-EDC together with its Container Images +Build Tractus-X EDC together with its Container Images ```shell ./gradlew dockerize diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 2f73afb7f..6ba7dc40c 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -262,7 +262,7 @@ spec: ## DATA PLANE ## ################ - # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/dataplane-selector-configuration + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/dataplane-selector-configuration - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" value: {{ include "txdc.dataplane.url.control" . }}/transfer - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" @@ -291,7 +291,7 @@ spec: ########### {{- if .Values.vault.hashicorp.enabled }} - # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault - name: "EDC_VAULT_HASHICORP_URL" value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} - name: "EDC_VAULT_HASHICORP_TOKEN" @@ -326,7 +326,7 @@ spec: ## DATA ENCRYPTION ## ##################### - # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/data-encryption + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/data-encryption - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} - name: "EDC_DATA_ENCRYPTION_ALGORITHM" diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index cafb50909..7f48345e0 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -132,7 +132,7 @@ spec: ########### {{- if .Values.vault.hashicorp.enabled }} - # see extension https://github.com/catenax-ng/product-edc/tree/develop/edc-extensions/hashicorp-vault + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault - name: "EDC_VAULT_HASHICORP_URL" value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} - name: "EDC_VAULT_HASHICORP_TOKEN" diff --git a/docs/README.md b/docs/README.md index 096e41feb..259c2560b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,7 @@ The Tractus-X EDC repository creates runnable applications out of EDC extensions from the [Eclipse DataSpace Connector](https://github.com/eclipse-edc/Connector) repository. -When running a EDC connector from the Product EDC repository there are three setups to choose from. They only vary by using different extensions for +When running a EDC connector from the Tractus-X EDC repository there are three setups to choose from. They only vary by using different extensions for - Resolving of Connector-Identities - Persistence of the Control-Plane-State diff --git a/docs/development/decision-records/2023-02-27_testing/README.md b/docs/development/decision-records/2023-02-27_testing/README.md index c4619eb8b..45844203d 100644 --- a/docs/development/decision-records/2023-02-27_testing/README.md +++ b/docs/development/decision-records/2023-02-27_testing/README.md @@ -1,4 +1,4 @@ -# Testing concept for product-edc +# Testing concept for tractusx-edc ## Decision @@ -13,7 +13,7 @@ Henceforth, testing shall be done in accordance with the herein outlined rules a ## Rationale -Past experiences with product-edc's testing setup has shown that it is time- and resource-consuming, which also makes it unreliable at times. +Past experiences with tractusx-edc's testing setup has shown that it is time- and resource-consuming, which also makes it unreliable at times. Furthermore, a finer-grained test classification such as the one outlined in this document is currently neither present nor documented. ### Definitions and distinction diff --git a/docs/development/decision-records/2023-03-02_gradle_build/README.md b/docs/development/decision-records/2023-03-02_gradle_build/README.md index 4012599c1..9d4461cc6 100644 --- a/docs/development/decision-records/2023-03-02_gradle_build/README.md +++ b/docs/development/decision-records/2023-03-02_gradle_build/README.md @@ -2,7 +2,7 @@ ## Decision -Product-EDC will move to Gradle as its build system. This decision +Tractus-X EDC will move to Gradle as its build system. This decision record outlines the reasoning behind the decision as well as the migration path. ## Rationale @@ -11,7 +11,7 @@ The primary motivator for migrating to Gradle is the overarching goal, set by th open-source methodology in general, and to track the Eclipse Datasource Components project in particular. While in theory that could be achieved with any build tool, much of what is useful or even necessary to achieve that goal, such as publishing to OSSRH/Sonatype and - in further consequence - to MavenCentral, has already been implemented in the EDC -project. This reduces the implementation and maintenance surface of product-edc with regard to the build, documentation +project. This reduces the implementation and maintenance surface of tractusx-edc with regard to the build, documentation and testing, and hence increases the development velocity considerably. It is therefore a foregone conclusion to rely on technology that has already proven itself in the opensource community, @@ -43,6 +43,6 @@ parallelization resulting in faster and more responsive builds. ## Further consideration -Planned improvements regarding the testing procedure (PR ) will also greatly benefit from the EDC build tools such +Planned improvements regarding the testing procedure will also greatly benefit from the EDC build tools such as JUnit tags and conditional evaluation of the tagged tests. Much of EDC's testing framework is based on Gradle and can -be seamlessly integrated in product-edc. +be seamlessly integrated in tractusx-edc. diff --git a/docs/development/postman/collection.json b/docs/development/postman/collection.json index 5f44e6ee5..50d0c5ab7 100644 --- a/docs/development/postman/collection.json +++ b/docs/development/postman/collection.json @@ -63,7 +63,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"asset\": {\n \"properties\": {\n \"asset:prop:id\": \"{{ASSET_ID}}\",\n \"asset:prop:description\": \"Product EDC Demo Asset\"\n }\n },\n \"dataAddress\": {\n \"properties\": {\n \"type\": \"HttpData\",\n \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\"\n }\n }\n}", + "raw": "{\n \"asset\": {\n \"properties\": {\n \"asset:prop:id\": \"{{ASSET_ID}}\",\n \"asset:prop:description\": \"Tractus-X EDC Demo Asset\"\n }\n },\n \"dataAddress\": {\n \"properties\": {\n \"type\": \"HttpData\",\n \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\"\n }\n }\n}", "options": { "raw": { "language": "json" diff --git a/docs/migration/Version_0.0.x_0.1.x.md b/docs/migration/Version_0.0.x_0.1.x.md index 353db9368..07ea746d9 100644 --- a/docs/migration/Version_0.0.x_0.1.x.md +++ b/docs/migration/Version_0.0.x_0.1.x.md @@ -18,7 +18,7 @@ This document contains a list of breaking changes that are introduced in version ## 1. PostgreSQL Database -The Product EDC [PostgreSQL Migration Extension](../../edc-extensions/postgresql-migration/README.md) is able to run +The Tractus-X EDC [PostgreSQL Migration Extension](../../edc-extensions/postgresql-migration/README.md) is able to run normal migrations. But the extension will never cause a data loss automatically, therefore part of this migration must be done by the user itself. @@ -285,7 +285,7 @@ property is mostly used when creating assets. #### Example Call ```bash -curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Product EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' +curl -X POST "$PLATO_DATAMGMT_URL/data/assets" --header "X-Api-Key: password" --header "Content-Type: application/json" --data "{ \"asset\": { \"properties\": { \"asset:prop:id\": \"1\", \"asset:prop:description\": \"Tractus-X EDC Demo Asset\" } }, \"dataAddress\": { \"properties\": { \"type\": \"HttpData\", \"baseUrl\": \"https://jsonplaceholder.typicode.com/todos/1\" } } }" -s -o /dev/null -w 'Response Code: %{http_code}\n' ``` ## 3. Connector Configuration diff --git a/docs/release-notes/Version 0.1.2.md b/docs/release-notes/Version 0.1.2.md index cef41cbd6..0f4babacd 100644 --- a/docs/release-notes/Version 0.1.2.md +++ b/docs/release-notes/Version 0.1.2.md @@ -8,11 +8,11 @@ The Git submodule references commit `740c100ac162bc41b1968c232ad81f7d739aefa9` from the 23th of September 2022 (newer than **0.0.1-milestone-6**). -## 2. Product EDC +## 2. Tractus-X EDC ### 2.1 Alpine Image -Introduce alpine image as base for all Product EDC Images (replaced distroless image). +Introduce alpine image as base for all Tractus-X EDC Images (replaced distroless image). ## 3. Fixed Issues diff --git a/docs/samples/Transfer Data.md b/docs/samples/Transfer Data.md index d68d87561..855efa8a0 100644 --- a/docs/samples/Transfer Data.md +++ b/docs/samples/Transfer Data.md @@ -175,7 +175,7 @@ curl -X POST "${BOB_DATAMGMT_URL}/data/assets" \ "asset": { "properties": { "asset:prop:id": "1", - "asset:prop:description": "Product EDC Demo Asset" + "asset:prop:description": "Tractus-X EDC Demo Asset" } }, "dataAddress": { diff --git a/edc-tests/cucumber/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml b/edc-tests/cucumber/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml index 567f48a74..289476122 100644 --- a/edc-tests/cucumber/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml +++ b/edc-tests/cucumber/src/main/resources/deployment/helm/omejdn/templates/deployment.yaml @@ -59,7 +59,7 @@ spec: cp /opt/config/scope_mapping.yml /etc/daps/scope_mapping.yml apk add --update openssl openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /etc/keys/omejdn/omejdn.key \ - -subj "/C=DE/ST=Berlin/L=Berlin/O=Product-EDC-Test, Inc./OU=DE" + -subj "/C=DE/ST=Berlin/L=Berlin/O=TractusX-EDC-Test, Inc./OU=DE" volumeMounts: - mountPath: /etc/daps name: config-dir From aa952ba5c36db61563f555fcb72d7752400da3d7 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Wed, 22 Mar 2023 11:14:13 +0100 Subject: [PATCH 006/132] Fix README.md and Transfer Data.md --- README.md | 86 +++++++++-------------------------- docs/samples/Transfer Data.md | 6 +-- 2 files changed, 24 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 6f3fc8232..2ca3a9f92 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,23 @@ - +# Tractus-X EDC (Eclipse Dataspace Connector) - [![Contributors][contributors-shield]][contributors-url] [![Stargazers][stars-shield]][stars-url] [![Apache 2.0 License][license-shield]][license-url] [![Latest Release][release-shield]][release-url] - -
-
- - Logo - - -

Product Eclipse Dataspace Connector

-

Catena-X

- -

- Container images and deployments of the Eclipse Dataspace Components open source project. -
- Explore the docs » -
-
- View Eclipse Dataspace Components - · - Releases - · - Report Bug / Request Feature -

-
- - -
- Table of Contents -
    -
  1. - About The Project -
  2. -
  3. - Inventory -
  4. -
  5. - Getting Started - -
  6. -
  7. License
  8. -
-
+Container images and deployments of the Eclipse Dataspace Components for the Tractus-X project. + +Please also refer to: + +- [Our docs](https://github.com/eclipse-tractusx/tractusx-edc/tree/main/docs) +- [Our Releases](https://github.com/eclipse-tractusx/tractusx-edc/releases) +- [Eclipse Dataspace Components](https://github.com/eclipse-edc/Connector) +- [Report Bug / Request Feature](https://github.com/eclipse-tractusx/tractusx-edc/issues) ## About The Project The project provides pre-built control- and data-plane [docker](https://www.docker.com/) images and [helm](https://helm.sh/) charts of the [Eclipse DataSpaceConnector Project](https://github.com/eclipse-edc/Connector). -

(back to top)

- ## Inventory The eclipse data space connector is split up into Control-Plane and Data-Plane, whereas the Control-Plane functions as administration layer @@ -64,28 +26,24 @@ The Data-Plane does the heavy lifting of transferring and receiving data streams Depending on your environment there are different derivatives of the control-plane prepared: -* [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) with dependency onto - * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) -* [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) with dependency onto - * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) - * [PostgreSQL 8.2 or newer](https://www.postgresql.org/) -* [edc-controlplane-postgresql-hashicorp-vault](edc-controlplane/edc-controlplane-postgresql-hashicorp-vault) with dependency onto - * [Hashicorp Vault](https://www.vaultproject.io/) - * [PostgreSQL 8.2 or newer](https://www.postgresql.org/) +- [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) with dependency onto + - [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) +- [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) with dependency onto + - [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) + - [PostgreSQL 8.2 or newer](https://www.postgresql.org/) +- [edc-controlplane-postgresql-hashicorp-vault](edc-controlplane/edc-controlplane-postgresql-hashicorp-vault) with dependency onto + - [Hashicorp Vault](https://www.vaultproject.io/) + -[PostgreSQL 8.2 or newer](https://www.postgresql.org/) Derivatives of the Data-Plane can be found here -* [edc-dataplane-azure-vault](edc-dataplane/edc-dataplane-azure-vault) with dependency onto - * [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) -* [edc-dataplane-hashicorp-vault](edc-dataplane/edc-dataplane-hashicorp-vault) with dependency onto - * [Hashicorp Vault](https://www.vaultproject.io/) - -

(back to top)

+- [edc-dataplane-azure-vault](edc-dataplane/edc-dataplane-azure-vault) with dependency onto + - [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) +- [edc-dataplane-hashicorp-vault](edc-dataplane/edc-dataplane-hashicorp-vault) with dependency onto + - [Hashicorp Vault](https://www.vaultproject.io/) ## Getting Started -

(back to top)

- ### Build Build Tractus-X EDC together with its Container Images @@ -100,8 +58,6 @@ Build Tractus-X EDC together with its Container Images Distributed under the Apache 2.0 License. See [LICENSE](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) for more information. -

(back to top)

- [contributors-shield]: https://img.shields.io/github/contributors/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge diff --git a/docs/samples/Transfer Data.md b/docs/samples/Transfer Data.md index 855efa8a0..4d467282a 100644 --- a/docs/samples/Transfer Data.md +++ b/docs/samples/Transfer Data.md @@ -6,18 +6,18 @@ For this transfer connector **Bob** will act as data provider, and connector **A consumer. But the roles could be inverse as well. > Please note: Before running the examples the corresponding environment variables must be set. -> How such an environment can be setup locally is documented in [chapter 1](#1--optional--local-setup). +> How such an environment can be setup locally is documented in [chapter 1](#1-optional---local-setup). ## Table of Content -1. [(optional) Local Setup](#1--optional--local-setup) +1. [Optional: Local Setup](#1-optional---local-setup) 2. [Setup Data Offer](#2-setup-data-offer) 3. [Request Contract Offers](#3-request-contract-offer-catalog) 4. [Negotiate Contract](#4-negotiate-contract) 5. [Transfer Data](#5-transfer-data) 6. [Verify Data Transfer](#6-verify-data-transfer) -## 1. (optional) Local Setup +## 1. Optional - Local Setup To create a local setup with two connectors have a look at the [Local TXDC Setup Documentation](Local%20TXDC%20Setup.md). From bc7a1aaf8e1d2a742f71c04e98bcdf409a274fc3 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Wed, 22 Mar 2023 11:16:20 +0100 Subject: [PATCH 007/132] Fix Transfer Data.md --- docs/samples/Transfer Data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/samples/Transfer Data.md b/docs/samples/Transfer Data.md index 4d467282a..97da48384 100644 --- a/docs/samples/Transfer Data.md +++ b/docs/samples/Transfer Data.md @@ -10,7 +10,7 @@ consumer. But the roles could be inverse as well. ## Table of Content -1. [Optional: Local Setup](#1-optional---local-setup) +1. [Optional - Local Setup](#1-optional---local-setup) 2. [Setup Data Offer](#2-setup-data-offer) 3. [Request Contract Offers](#3-request-contract-offer-catalog) 4. [Negotiate Contract](#4-negotiate-contract) From 9edbfc648a4567b3548471fe12a894fc2a4eb352 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 23 Mar 2023 06:44:58 +0100 Subject: [PATCH 008/132] Regenerate helm chart README.md files --- charts/edc-controlplane/Chart.yaml | 2 +- charts/edc-controlplane/README.md | 8 ++++++-- charts/edc-dataplane/Chart.yaml | 2 +- charts/edc-dataplane/README.md | 8 ++++++-- charts/tractusx-connector/Chart.yaml | 26 ++++++++++++++++++++++++++ charts/tractusx-connector/README.md | 22 ++++++++++++++++------ charts/tractusx-connector/values.yaml | 4 ++-- 7 files changed, 58 insertions(+), 14 deletions(-) diff --git a/charts/edc-controlplane/Chart.yaml b/charts/edc-controlplane/Chart.yaml index e0ec00697..f80a6587d 100644 --- a/charts/edc-controlplane/Chart.yaml +++ b/charts/edc-controlplane/Chart.yaml @@ -32,4 +32,4 @@ version: 0.3.0 deprecated: true maintainers: [] sources: - - https://github.com/eclipse-tractusx/tractusx-edc + - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-controlplane diff --git a/charts/edc-controlplane/README.md b/charts/edc-controlplane/README.md index 2e2a0cf68..15e61cff0 100644 --- a/charts/edc-controlplane/README.md +++ b/charts/edc-controlplane/README.md @@ -6,7 +6,7 @@ EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers -- **Homepage:** +**Homepage:** ## TL;DR @@ -15,6 +15,10 @@ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev helm install my-release tractusx-edc/edc-controlplane --version 0.3.0 ``` +## Source Code + +* + ## Values | Key | Type | Default | Description | @@ -44,7 +48,7 @@ helm install my-release tractusx-edc/edc-controlplane --version 0.3.0 | envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-memory] | +| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-memory] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/charts/edc-dataplane/Chart.yaml b/charts/edc-dataplane/Chart.yaml index 001fe2d1b..9d051fb7d 100644 --- a/charts/edc-dataplane/Chart.yaml +++ b/charts/edc-dataplane/Chart.yaml @@ -32,4 +32,4 @@ version: 0.3.0 deprecated: true maintainers: [] sources: - - https://github.com/eclipse-tractusx/tractusx-edc + - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-dataplane diff --git a/charts/edc-dataplane/README.md b/charts/edc-dataplane/README.md index 934ff72c1..6776e76fa 100644 --- a/charts/edc-dataplane/README.md +++ b/charts/edc-dataplane/README.md @@ -6,7 +6,7 @@ EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams -- **Homepage:** +**Homepage:** ## TL;DR @@ -15,6 +15,10 @@ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev helm install my-release tractusx-edc/edc-dataplane --version 0.3.0 ``` +## Source Code + +* + ## Values | Key | Type | Default | Description | @@ -40,7 +44,7 @@ helm install my-release tractusx-edc/edc-dataplane --version 0.3.0 | envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-azure-vault] | +| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index 7057f1599..d611d8944 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + --- apiVersion: v2 name: tractusx-connector @@ -20,3 +42,7 @@ version: 0.3.0 # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "0.3.0" + +home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector +sources: + - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector \ No newline at end of file diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index ccd0cae09..5f160a7b2 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -4,6 +4,8 @@ A Helm chart for Tractus-X Eclipse Data Space Connector +**Homepage:** + ## TL;DR ```shell @@ -11,6 +13,10 @@ helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev helm install my-release tractusx-edc/tractusx-connector --version 0.3.0 ``` +## Source Code + +* + ## Values | Key | Type | Default | Description | @@ -25,7 +31,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.0 | controlplane.debug.enabled | bool | `false` | | | controlplane.debug.port | int | `1044` | | | controlplane.debug.suspendOnStart | bool | `false` | | -| controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"data":{"authKey":"","path":"/data","port":8081},"default":{"path":"/api","port":8080},"ids":{"path":"/api/v1/ids","port":8084},"metrics":{"path":"/metrics","port":8085},"validation":{"path":"/validation","port":8082}}` | endpoints of the control plane | +| controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"data":{"authKey":"","path":"/data","port":8081},"default":{"path":"/api","port":8080},"ids":{"path":"/api/v1/ids","port":8084},"metrics":{"path":"/metrics","port":9090},"observability":{"insecure":true,"path":"/observability","port":8085},"validation":{"path":"/validation","port":8082}}` | endpoints of the control plane | | controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | | controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | | controlplane.endpoints.control.port | int | `8083` | port for incoming api calls | @@ -39,9 +45,13 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.0 | controlplane.endpoints.ids | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | | controlplane.endpoints.ids.path | string | `"/api/v1/ids"` | path for incoming api calls | | controlplane.endpoints.ids.port | int | `8084` | port for incoming api calls | -| controlplane.endpoints.metrics | object | `{"path":"/metrics","port":8085}` | metrics api, used for application metrics, must not be internet facing | +| controlplane.endpoints.metrics | object | `{"path":"/metrics","port":9090}` | metrics api, used for application metrics, must not be internet facing | | controlplane.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | -| controlplane.endpoints.metrics.port | int | `8085` | port for incoming api calls | +| controlplane.endpoints.metrics.port | int | `9090` | port for incoming api calls | +| controlplane.endpoints.observability | object | `{"insecure":true,"path":"/observability","port":8085}` | observability api with unsecured access, must not be internet facing | +| controlplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | +| controlplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | +| controlplane.endpoints.observability.port | int | `8085` | port for incoming API calls | | controlplane.endpoints.validation | object | `{"path":"/validation","port":8082}` | validation api, only used by the data plane and should not be added to any ingress | | controlplane.endpoints.validation.path | string | `"/validation"` | path for incoming api calls | | controlplane.endpoints.validation.port | int | `8082` | port for incoming api calls | @@ -85,7 +95,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.0 | controlplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | | controlplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | | controlplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| controlplane.logging | string | `".level=INFO\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| controlplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | | controlplane.nodeSelector | object | `{}` | | | controlplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | | controlplane.podAnnotations | object | `{}` | additional annotations for the pod | @@ -137,7 +147,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.0 | dataplane.endpoints.default.path | string | `"/api"` | | | dataplane.endpoints.default.port | int | `8080` | | | dataplane.endpoints.metrics.path | string | `"/metrics"` | | -| dataplane.endpoints.metrics.port | int | `8084` | | +| dataplane.endpoints.metrics.port | int | `9090` | | | dataplane.endpoints.public.path | string | `"/api/public"` | | | dataplane.endpoints.public.port | int | `8081` | | | dataplane.endpoints.validation.path | string | `"/validation"` | | @@ -166,7 +176,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.0 | dataplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | | dataplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | | dataplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| dataplane.logging | string | `".level=INFO\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| dataplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | | dataplane.nodeSelector | object | `{}` | | | dataplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | | dataplane.podAnnotations | object | `{}` | additional annotations for the pod | diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index b314004c3..6063f96e5 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -273,7 +273,7 @@ controlplane: # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) logging: |- .level=INFO - org.eclipse.dataspaceconnector.level=ALL + org.eclipse.edc.level=ALL handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter java.util.logging.ConsoleHandler.level=ALL @@ -470,7 +470,7 @@ dataplane: # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) logging: |- .level=INFO - org.eclipse.dataspaceconnector.level=ALL + org.eclipse.edc.level=ALL handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter java.util.logging.ConsoleHandler.level=ALL From f34c84f113dc4e7ec1736a7c394cd801efd9fb69 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 23 Mar 2023 06:46:05 +0100 Subject: [PATCH 009/132] Remove left over html tags from root REAMDE.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 2ca3a9f92..566e42f5a 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,6 @@ Build Tractus-X EDC together with its Container Images ./gradlew dockerize ``` -

(back to top)

- ## License Distributed under the Apache 2.0 License. See [LICENSE](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) for more information. From f9c7682f89adf509e1c50c31781207841e9a7bdf Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 23 Mar 2023 06:51:21 +0100 Subject: [PATCH 010/132] Add empty line at EOF --- charts/tractusx-connector/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index d611d8944..c86990c6a 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -45,4 +45,4 @@ appVersion: "0.3.0" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector sources: - - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector \ No newline at end of file + - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector From 58205ba524374d9707e76d6e40e3fe8af2d955fd Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 23 Mar 2023 07:06:41 +0100 Subject: [PATCH 011/132] Update CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 651d7656a..73b8aa525 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -43,4 +43,4 @@ Project committers or leaders who do not follow the Code of Conduct in good fait ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org) , version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) From b2a015600e2116220b3bc641ce8927bf158ffb70 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 23 Mar 2023 07:40:21 +0100 Subject: [PATCH 012/132] Retrigger ci From 2dbcaec038d1b68bc0d74c9b1cf7d889b18a7d79 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Mon, 27 Mar 2023 17:52:00 +0200 Subject: [PATCH 013/132] Release: fix version handling --- .github/workflows/build.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 6 +++--- build.gradle.kts | 7 ------- gradle.properties | 2 +- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 78b91b6f6..55e1860c6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -254,7 +254,7 @@ jobs: # publish snapshots - name: Publish snapshot versions run: |- - echo "Publishing Version $(grep -e "defaultVersion" gradle.properties | cut -f2 -d"=") to Github Packages" + echo "Publishing Version $(grep -e "version" gradle.properties | cut -f2 -d"=") to Github Packages" ./gradlew publishAllPublicationsToGitHubPackagesRepository env: REPO: ${{ github.repository }} diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 9c4e888c8..a7c618f45 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -43,7 +43,7 @@ jobs: name: Bump version in gradle.properties run: |- # replace the project's (default) version, could be overwritten later with the -Pversion=... flag - sed -i 's/defaultVersion=.*/defaultVersion=${{ github.event.inputs.version }}/g' gradle.properties + sed -i 's/version=.*/version=${{ github.event.inputs.version }}/g' gradle.properties env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index d2064264f..16192638b 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -72,7 +72,7 @@ jobs: - name: Publish release version run: | - echo "Publishing Version $(grep -e "defaultVersion" gradle.properties | cut -f2 -d"=") to Github Packages" + echo "Publishing Version $(grep -e "version" gradle.properties | cut -f2 -d"=") to Github Packages" ./gradlew publishAllPublicationsToGithubPackagesRepository env: REPO: ${{ github.repository }} @@ -200,8 +200,8 @@ jobs: VERSION="$RELEASE_VERSION_MAJOR.$RELEASE_VERSION_MINOR.$((RELEASE_VERSION_PATCH+1))-SNAPSHOT" SNAPSHOT_VERSION=$VERSION - # Persist the "defaultVersion" in the gradle.properties - sed -i 's/defaultVersion=.*/defaultVersion=${{ github.event.inputs.version }}/g' gradle.properties + # Persist the "version" in the gradle.properties + sed -i 's/version=.*/version=${{ github.event.inputs.version }}/g' gradle.properties # Commit and push to origin develop diff --git a/build.gradle.kts b/build.gradle.kts index 04e7ff7cc..ba0f247dd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,15 +17,9 @@ val txScmConnection: String by project val txWebsiteUrl: String by project val txScmUrl: String by project val groupId: String by project -val defaultVersion: String by project val annotationProcessorVersion: String by project val metaModelVersion: String by project -var actualVersion: String = (project.findProperty("version") ?: defaultVersion) as String -if (actualVersion == "unspecified") { - actualVersion = defaultVersion -} - buildscript { repositories { mavenLocal() @@ -73,7 +67,6 @@ allprojects { configure { versions { // override default dependency versions here - projectVersion.set(actualVersion) metaModel.set(metaModelVersion) } diff --git a/gradle.properties b/gradle.properties index 1c45f2373..f126bae28 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ groupId=org.eclipse.tractusx.edc -defaultVersion=0.3.1-SNAPSHOT +version=0.3.1-SNAPSHOT javaVersion=11 # configure the build: From 44005001f5d9048f02ce2c6cdd511ff0dedf968c Mon Sep 17 00:00:00 2001 From: GitHub actions Date: Mon, 27 Mar 2023 16:00:34 +0000 Subject: [PATCH 014/132] Prepare release 0.3.1 --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4674ba285..313f312c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.3.1] - 2023-03-27 + ### Added ### Changed @@ -250,7 +252,11 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ## [0.0.1] - 2022-05-13 -[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.1.6...HEAD +[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.1...HEAD + +[0.3.1]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.0...0.3.1 + +[0.3.0]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.2.0...0.3.0 [0.1.6]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.1.5...0.1.6 From aa789b26936c3d6b74646c1791203c71ef7d269a Mon Sep 17 00:00:00 2001 From: Stephan Bauer Date: Wed, 1 Mar 2023 14:16:07 +0100 Subject: [PATCH 015/132] Cherry-picked upstream commits (QGate stuff) in preparation for the 0.3.1 release --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- charts/edc-controlplane/.helmignore | 4 + charts/edc-controlplane/LICENSE | 202 ++++++++++++++++++++++ charts/edc-controlplane/README.md | 2 +- charts/edc-controlplane/values.yaml | 4 +- charts/edc-dataplane/.helmignore | 4 + charts/edc-dataplane/LICENSE | 202 ++++++++++++++++++++++ charts/edc-dataplane/README.md | 2 +- charts/edc-dataplane/values.yaml | 4 +- misc/NOTICE.md.template | 21 +++ 10 files changed, 440 insertions(+), 7 deletions(-) create mode 100644 charts/edc-controlplane/LICENSE create mode 100644 charts/edc-dataplane/LICENSE create mode 100644 misc/NOTICE.md.template diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 62c89ee8c..85ebda849 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,7 +7,7 @@ assignees: '' --- -_If you are missing a feature or have an idea how to improve this project that should first be +_If you are missing a feature or have an idea how to improve this project that should first be discussed, please feel free to open up a [discussion](https://github.com/eclipse-tractusx/tractusx-edc/discussions/categories/ideas)._ **Is your feature request related to a problem? Please describe.** diff --git a/charts/edc-controlplane/.helmignore b/charts/edc-controlplane/.helmignore index 00ca644b2..148b31d6c 100644 --- a/charts/edc-controlplane/.helmignore +++ b/charts/edc-controlplane/.helmignore @@ -23,3 +23,7 @@ .vscode/ README.md.gotmpl + +# Accept only values.yaml +values?*.yaml +values?*.yml diff --git a/charts/edc-controlplane/LICENSE b/charts/edc-controlplane/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/charts/edc-controlplane/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/edc-controlplane/README.md b/charts/edc-controlplane/README.md index 15e61cff0..17c6dd45d 100644 --- a/charts/edc-controlplane/README.md +++ b/charts/edc-controlplane/README.md @@ -48,7 +48,7 @@ helm install my-release tractusx-edc/edc-controlplane --version 0.3.0 | envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-memory] | +| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-memory] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/charts/edc-controlplane/values.yaml b/charts/edc-controlplane/values.yaml index f3443773f..b43d67a35 100644 --- a/charts/edc-controlplane/values.yaml +++ b/charts/edc-controlplane/values.yaml @@ -31,8 +31,8 @@ replicaCount: 1 image: # -- Which derivate of the edc control-plane to use. - # One of: [ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql, ghcr.io/catenax-ng/product-edc/edc-controlplane-memory] - repository: ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault + # One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-memory] + repository: ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion. diff --git a/charts/edc-dataplane/.helmignore b/charts/edc-dataplane/.helmignore index 00ca644b2..148b31d6c 100644 --- a/charts/edc-dataplane/.helmignore +++ b/charts/edc-dataplane/.helmignore @@ -23,3 +23,7 @@ .vscode/ README.md.gotmpl + +# Accept only values.yaml +values?*.yaml +values?*.yml diff --git a/charts/edc-dataplane/LICENSE b/charts/edc-dataplane/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/charts/edc-dataplane/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/charts/edc-dataplane/README.md b/charts/edc-dataplane/README.md index 6776e76fa..76424d13d 100644 --- a/charts/edc-dataplane/README.md +++ b/charts/edc-dataplane/README.md @@ -44,7 +44,7 @@ helm install my-release tractusx-edc/edc-dataplane --version 0.3.0 | envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | | fullnameOverride | string | `""` | Overrides the releases full name | | image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault] | +| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-azure-vault] | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | | imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | | imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | diff --git a/charts/edc-dataplane/values.yaml b/charts/edc-dataplane/values.yaml index 926032306..9a049cb1f 100644 --- a/charts/edc-dataplane/values.yaml +++ b/charts/edc-dataplane/values.yaml @@ -31,8 +31,8 @@ replicaCount: 1 image: # -- Which derivate of the edc data-plane to use. - # One of: [ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault, ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault] - repository: ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault + # One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-azure-vault] + repository: ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion diff --git a/misc/NOTICE.md.template b/misc/NOTICE.md.template new file mode 100644 index 000000000..d49cb8eea --- /dev/null +++ b/misc/NOTICE.md.template @@ -0,0 +1,21 @@ +# Notices for Catena-X NG Product EDC + +## Copyright + +All content is the property of the respective authors or their employers. For more information regarding authorship of content, please consult the listed source code repository logs. + +## Declared Project Licenses + +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 + +## Source Code + +The project maintains the following source code repositoriy: + +* https://github.com/eclipse-tractusx/tractusx-edc + +## Third-party Content (Overarching All Modules) + +@{GENERATED_NOTICES} \ No newline at end of file From d29620186fac09b5505454dddb63aa1c4f80dc75 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Tue, 28 Mar 2023 08:16:25 +0200 Subject: [PATCH 016/132] fix: use snapshot version after publish workflow --- .github/workflows/publish-new-release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 16192638b..b688219fe 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -201,8 +201,7 @@ jobs: SNAPSHOT_VERSION=$VERSION # Persist the "version" in the gradle.properties - sed -i 's/version=.*/version=${{ github.event.inputs.version }}/g' gradle.properties - + sed -i "s/version=.*/version=$SNAPSHOT_VERSION/g" gradle.properties # Commit and push to origin develop git add gradle.properties From b46b310596b2b440a0db1ecc9b6d9d464db78732 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Fri, 24 Mar 2023 17:31:47 +0100 Subject: [PATCH 017/132] docs: add additional info for running business tests locally --- docs/development/Run-business-tests-local.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/development/Run-business-tests-local.md b/docs/development/Run-business-tests-local.md index 9d66f7bbc..76520007d 100644 --- a/docs/development/Run-business-tests-local.md +++ b/docs/development/Run-business-tests-local.md @@ -14,7 +14,7 @@ Prerequisites: ## 2. Install the all-in-one supporting infrastructure environment (Daps, Vault, PostgreSql, Minio, Backend-Service) ```shel -helm install infrastructure edc-tests/src/main/resources/deployment/helm/supporting-infrastructure -n business-tests --create-namespace +helm install infrastructure edc-tests/src/main/resources/deployment/helm/supporting-infrastructure -n business-tests --dependency-update --create-namespace ``` To access the PostgreSql databases you could use following kubectl port forwardings: @@ -139,8 +139,15 @@ kubectl get svc -n business-tests -o go-template='{{range .items}}{{ $save := . This will return all NodePorts which are available in business-tests namespace where you can pick the ports to use in your environment variables. Now you are able to run it in IDE either as normal "Run" mode or in "Debug" mode where you can debug the business-tests by setting debugging points. -## 6. Update your components +Example of mapping to environment variables needed for the business tests: +```shell +business-tests/plato-controlplane - data: 30955(8081) -> PLATO_DATA_MANAGEMENT_URL=http://localhost:30955/data; +business-tests/sokrates-controlplane - data: 30538(8081) -> SOKRATES_DATA_MANAGEMENT_URL=http://localhost:30538/data; +business-tests/backend - backend: 30556(8081) -> SOKRATES_BACKEND_SERVICE_BACKEND_API_URL= http://localhost:30556 +``` + +### 6. Update your components Once everything is installed you just need to update your services when you have a new image. ```shell From b20ddaa3cab8cc6dca2c79dd75ba470ca6ff6b21 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Tue, 21 Mar 2023 16:22:19 +0100 Subject: [PATCH 018/132] feat(CI): add Markdown linter --- .github/workflows/markdown-lint.yaml | 43 ++++++++++++++++++++++++++++ .markdownlint.yaml | 26 +++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 .github/workflows/markdown-lint.yaml create mode 100644 .markdownlint.yaml diff --git a/.github/workflows/markdown-lint.yaml b/.github/workflows/markdown-lint.yaml new file mode 100644 index 000000000..34fdc55ea --- /dev/null +++ b/.github/workflows/markdown-lint.yaml @@ -0,0 +1,43 @@ +#******************************************************************************** +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +#*******************************************************************************/ + +name: "Lint Markdown" + +on: + push: + branches: + - main + - develop + pull_request: + branches: + - main + - develop + +jobs: + markdown-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install mardkdownlint + run: npm install -g markdownlint-cli2 + + - name: Run markdownlint + run: | + markdownlint-cli2-config .markdownlint.yaml "**/*.md" "#node_modules" diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 000000000..a5aa0776a --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,26 @@ +#******************************************************************************** +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +#*******************************************************************************/ + + +"default": true +# Do not restrict line length: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#MD013 +"MD013": false +# Allow same content on headlines on siblings: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#MD024 +"MD024": + "siblings_only": true From c2c936ce9cf8bf83eb44c190122cddde9f58b187 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Tue, 28 Mar 2023 11:05:13 +0200 Subject: [PATCH 019/132] md lint fix --- .../decision-records/2023-03-23_remove_lombok/README.md | 4 ++-- edc-tests/cucumber/README.md | 2 +- edc-tests/e2e-tests/README.md | 2 +- edc-tests/runtime/README.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/development/decision-records/2023-03-23_remove_lombok/README.md b/docs/development/decision-records/2023-03-23_remove_lombok/README.md index 74938d373..b7014c8b8 100644 --- a/docs/development/decision-records/2023-03-23_remove_lombok/README.md +++ b/docs/development/decision-records/2023-03-23_remove_lombok/README.md @@ -10,7 +10,7 @@ Lombok uses byte-code modification to achieve its goal. That is dangerous for a First and foremost, to achieve its goal, it relies on internal APIs of the JVM, which are not intended for public consumption, thus they can and will get removed, refactored or made otherwise unavailable. This has been discussed at -length in the [project's GitHub page](https://github.com/projectlombok/lombok/issues/2681). +length in the [project's GitHub page](https://github.com/projectlombok/lombok/issues/2681). This is especially problematic for an OSS project such as TractusX. Second, many of the features that are currently used by TractusX-EDC are experimental (e.g. `@UtilityClass`) and are @@ -34,4 +34,4 @@ should not build those obstructions into the code base. ## Further consideration -We can even expect a slightly faster build, because "delomboking" will become unnecessary. \ No newline at end of file +We can even expect a slightly faster build, because "delomboking" will become unnecessary. diff --git a/edc-tests/cucumber/README.md b/edc-tests/cucumber/README.md index e8c1a8ab1..db14876f7 100644 --- a/edc-tests/cucumber/README.md +++ b/edc-tests/cucumber/README.md @@ -1,6 +1,6 @@ # Invoke Business-Tests via Maven -THIS MODULE IS DEPRECATED AND WILL NOT BE MAINTAINED ANYMORE. +THIS MODULE IS DEPRECATED AND WILL NOT BE MAINTAINED ANYMORE. ```shell ./gradlew :edc-tests:test -Dcucumber=true diff --git a/edc-tests/e2e-tests/README.md b/edc-tests/e2e-tests/README.md index cdde986b0..f204147c3 100644 --- a/edc-tests/e2e-tests/README.md +++ b/edc-tests/e2e-tests/README.md @@ -1,3 +1,3 @@ # E2E-Tests -This module contains JUnit tests that spin up multiple runtimes in one JVM. \ No newline at end of file +This module contains JUnit tests that spin up multiple runtimes in one JVM. diff --git a/edc-tests/runtime/README.md b/edc-tests/runtime/README.md index 703963687..2f9593a75 100644 --- a/edc-tests/runtime/README.md +++ b/edc-tests/runtime/README.md @@ -1,3 +1,3 @@ # In-Memory Runtime for Testing Purposes -This module provides a very small, purely in-mem runtime to execute tests against. Not intended for anything other than testing! \ No newline at end of file +This module provides a very small, purely in-mem runtime to execute tests against. Not intended for anything other than testing! From b358e785a2b47c089190138248d1892943940793 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Tue, 28 Mar 2023 11:41:27 +0200 Subject: [PATCH 020/132] pr remarks --- .github/workflows/markdown-lint.yaml | 43 ---------------------------- .github/workflows/verify.yaml | 12 ++++++++ .markdownlint.yaml | 2 -- 3 files changed, 12 insertions(+), 45 deletions(-) delete mode 100644 .github/workflows/markdown-lint.yaml diff --git a/.github/workflows/markdown-lint.yaml b/.github/workflows/markdown-lint.yaml deleted file mode 100644 index 34fdc55ea..000000000 --- a/.github/workflows/markdown-lint.yaml +++ /dev/null @@ -1,43 +0,0 @@ -#******************************************************************************** -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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. -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -#*******************************************************************************/ - -name: "Lint Markdown" - -on: - push: - branches: - - main - - develop - pull_request: - branches: - - main - - develop - -jobs: - markdown-lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Install mardkdownlint - run: npm install -g markdownlint-cli2 - - - name: Run markdownlint - run: | - markdownlint-cli2-config .markdownlint.yaml "**/*.md" "#node_modules" diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index adfeb5558..a02f7514d 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -80,6 +80,18 @@ jobs: ./gradlew checkstyleMain checkstyleTest echo "Running Checkstyle is currently a placeholder" + markdown-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install mardkdownlint + run: npm install -g markdownlint-cli2 + + - name: Run markdownlint + run: | + markdownlint-cli2-config .markdownlint.yaml "**/*.md" "#node_modules" + unit-tests: runs-on: ubuntu-latest needs: [verify-formatting] diff --git a/.markdownlint.yaml b/.markdownlint.yaml index a5aa0776a..ace38e3d4 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -1,4 +1,3 @@ -#******************************************************************************** # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # # See the NOTICE file(s) distributed with this work for additional @@ -15,7 +14,6 @@ # under the License. # # SPDX-License-Identifier: Apache-2.0 -#*******************************************************************************/ "default": true From 4b15b473cd84dcc652acd64171ffbbab4c68044d Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Tue, 28 Mar 2023 11:45:03 +0200 Subject: [PATCH 021/132] Apply suggestions from code review Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- misc/NOTICE.md.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 85ebda849..62c89ee8c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,7 +7,7 @@ assignees: '' --- -_If you are missing a feature or have an idea how to improve this project that should first be +_If you are missing a feature or have an idea how to improve this project that should first be discussed, please feel free to open up a [discussion](https://github.com/eclipse-tractusx/tractusx-edc/discussions/categories/ideas)._ **Is your feature request related to a problem? Please describe.** diff --git a/misc/NOTICE.md.template b/misc/NOTICE.md.template index d49cb8eea..d01a9d8d5 100644 --- a/misc/NOTICE.md.template +++ b/misc/NOTICE.md.template @@ -1,4 +1,4 @@ -# Notices for Catena-X NG Product EDC +# Notices for Tractus-X EDC ## Copyright From 15ca06fbd8bae88201fb5d5c0364aadac7e47b41 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Tue, 28 Mar 2023 11:54:13 +0200 Subject: [PATCH 022/132] Update .github/workflows/verify.yaml Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- .github/workflows/verify.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index a02f7514d..3b5f56250 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -90,7 +90,7 @@ jobs: - name: Run markdownlint run: | - markdownlint-cli2-config .markdownlint.yaml "**/*.md" "#node_modules" + markdownlint-cli2-config .markdownlint.yaml "**/*.md" unit-tests: runs-on: ubuntu-latest From 35527230ca76af4cceba843387b1a8e59180ce0c Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Tue, 28 Mar 2023 16:58:41 +0200 Subject: [PATCH 023/132] chore(md-linting): Fix markdown lint --- docs/development/Run-business-tests-local.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/development/Run-business-tests-local.md b/docs/development/Run-business-tests-local.md index 76520007d..cab17c6c2 100644 --- a/docs/development/Run-business-tests-local.md +++ b/docs/development/Run-business-tests-local.md @@ -147,7 +147,8 @@ business-tests/sokrates-controlplane - data: 30538(8081) -> SOKRATES_DATA_MANAGE business-tests/backend - backend: 30556(8081) -> SOKRATES_BACKEND_SERVICE_BACKEND_API_URL= http://localhost:30556 ``` -### 6. Update your components +## 6. Update your components + Once everything is installed you just need to update your services when you have a new image. ```shell From bbe47808699a691bab573e73ee60d57e51c77b81 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 29 Mar 2023 20:51:52 +0200 Subject: [PATCH 024/132] fix: make AZKV clientsecret or certificate mutually exclusive --- .../templates/deployment-controlplane.yaml | 46 +++++++++++-------- charts/tractusx-connector/values.yaml | 4 +- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 6ba7dc40c..9f8bd9e3e 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -1,24 +1,24 @@ # -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # 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 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # --- apiVersion: apps/v1 @@ -315,10 +315,16 @@ spec: value: {{ .Values.vault.azure.tenant | required ".Values.vault.azure.tenant is required" | quote }} - name: "EDC_VAULT_NAME" value: {{ .Values.vault.azure.name | required ".Values.vault.azure.name is required" | quote }} + # only set the env var if config value not null + {{- if .Values.vault.azure.secret }} - name: "EDC_VAULT_CLIENTSECRET" value: {{ .Values.vault.azure.secret | quote }} + {{- end }} + # only set the env var if config value not null + {{- if .Values.vault.azure.certificate }} - name: "EDC_VAULT_CERTIFICATE" value: {{ .Values.vault.azure.certificate | quote }} + {{- end }} {{- end }} ##################### diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index 6063f96e5..cbc266a94 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -509,8 +509,8 @@ vault: name: "" client: "" tenant: "" - secret: "" - certificate: "" + secret: + certificate: secretNames: transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key From d77e6277bd2cdcc899fc4e3b5c518e4d0174deaa Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 30 Mar 2023 08:40:41 +0200 Subject: [PATCH 025/132] revert pointless blanks --- .../templates/deployment-controlplane.yaml | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 9f8bd9e3e..88320d681 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -1,24 +1,24 @@ # - # Copyright (c) 2023 ZF Friedrichshafen AG - # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH - # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation - # - # See the NOTICE file(s) distributed with this work for additional - # information regarding copyright ownership. - # - # 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 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - # License for the specific language governing permissions and limitations - # under the License. - # - # SPDX-License-Identifier: Apache-2.0 - # +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# --- apiVersion: apps/v1 From 27d776f8e0c016ec1be2f6359fee3c7caea09489 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 29 Mar 2023 14:55:00 +0200 Subject: [PATCH 026/132] fix: use correct paths for GH Packages docker reg. --- charts/edc-controlplane/Chart.yaml | 4 ++-- charts/edc-controlplane/README.md | 4 ++-- charts/edc-dataplane/Chart.yaml | 4 ++-- charts/edc-dataplane/README.md | 4 ++-- charts/tractusx-connector/Chart.yaml | 4 ++-- charts/tractusx-connector/README.md | 4 ++-- .../templates/deployment-controlplane.yaml | 8 ++++---- .../templates/deployment-dataplane.yaml | 4 ++-- .../decision-records/2023-02-09-release-process/README.md | 4 ++-- gradle.properties | 2 +- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/charts/edc-controlplane/Chart.yaml b/charts/edc-controlplane/Chart.yaml index f80a6587d..bf2ebea38 100644 --- a/charts/edc-controlplane/Chart.yaml +++ b/charts/edc-controlplane/Chart.yaml @@ -27,8 +27,8 @@ description: >- EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-controlplane type: application -appVersion: "0.3.0" -version: 0.3.0 +appVersion: "0.3.1" +version: 0.3.1 deprecated: true maintainers: [] sources: diff --git a/charts/edc-controlplane/README.md b/charts/edc-controlplane/README.md index 17c6dd45d..9099e29d9 100644 --- a/charts/edc-controlplane/README.md +++ b/charts/edc-controlplane/README.md @@ -2,7 +2,7 @@ > **:exclamation: This Helm Chart is deprecated!** -![Version: 0.3.0](https://img.shields.io/badge/Version-0.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.0](https://img.shields.io/badge/AppVersion-0.3.0-informational?style=flat-square) +![Version: 0.3.1](https://img.shields.io/badge/Version-0.3.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.1](https://img.shields.io/badge/AppVersion-0.3.1-informational?style=flat-square) EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers @@ -12,7 +12,7 @@ EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with res ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-controlplane --version 0.3.0 +helm install my-release tractusx-edc/edc-controlplane --version 0.3.1 ``` ## Source Code diff --git a/charts/edc-dataplane/Chart.yaml b/charts/edc-dataplane/Chart.yaml index 9d051fb7d..247d25c80 100644 --- a/charts/edc-dataplane/Chart.yaml +++ b/charts/edc-dataplane/Chart.yaml @@ -27,8 +27,8 @@ description: >- EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-dataplane type: application -appVersion: "0.3.0" -version: 0.3.0 +appVersion: "0.3.1" +version: 0.3.1 deprecated: true maintainers: [] sources: diff --git a/charts/edc-dataplane/README.md b/charts/edc-dataplane/README.md index 76424d13d..4eade6494 100644 --- a/charts/edc-dataplane/README.md +++ b/charts/edc-dataplane/README.md @@ -2,7 +2,7 @@ > **:exclamation: This Helm Chart is deprecated!** -![Version: 0.3.0](https://img.shields.io/badge/Version-0.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.0](https://img.shields.io/badge/AppVersion-0.3.0-informational?style=flat-square) +![Version: 0.3.1](https://img.shields.io/badge/Version-0.3.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.1](https://img.shields.io/badge/AppVersion-0.3.1-informational?style=flat-square) EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams @@ -12,7 +12,7 @@ EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility o ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-dataplane --version 0.3.0 +helm install my-release tractusx-edc/edc-dataplane --version 0.3.1 ``` ## Source Code diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index c86990c6a..0c8111bed 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -36,12 +36,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.0 +version: 0.3.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.3.0" +appVersion: "0.3.1" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector sources: diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index 5f160a7b2..52d4227a8 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -1,6 +1,6 @@ # tractusx-connector -![Version: 0.3.0](https://img.shields.io/badge/Version-0.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.0](https://img.shields.io/badge/AppVersion-0.3.0-informational?style=flat-square) +![Version: 0.3.1](https://img.shields.io/badge/Version-0.3.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.1](https://img.shields.io/badge/AppVersion-0.3.1-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector @@ -10,7 +10,7 @@ A Helm chart for Tractus-X Eclipse Data Space Connector ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector --version 0.3.0 +helm install my-release tractusx-edc/tractusx-connector --version 0.3.1 ``` ## Source Code diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 6ba7dc40c..67a3ab251 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -62,13 +62,13 @@ spec: {{- if .Values.controlplane.image.repository }} image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.postgresql.enabled .Values.vault.hashicorp.enabled }} - image: "ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.postgresql.enabled .Values.vault.azure.enabled }} - image: "ghcr.io/catenax-ng/product-edc/edc-controlplane-postgresql-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-postgresql:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.hashicorp.enabled }} - image: "ghcr.io/catenax-ng/product-edc/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.azure.enabled }} - image: "ghcr.io/catenax-ng/product-edc/edc-controlplane-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else }} {{- fail "cannot choose control-plane image automatically based on configuration" }} {{- end }} diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index 7f48345e0..ff5f6a5ce 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -40,9 +40,9 @@ spec: {{- if .Values.dataplane.image.repository }} image: "{{ .Values.dataplane.image.repository }}:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.vault.hashicorp }} - image: "ghcr.io/catenax-ng/product-edc/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.azure }} - image: "ghcr.io/catenax-ng/product-edc/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else }} {{- fail "cannot choose data-plane image automatically based on configuration" }} {{- end }} diff --git a/docs/development/decision-records/2023-02-09-release-process/README.md b/docs/development/decision-records/2023-02-09-release-process/README.md index c67521eb6..aee5bac5a 100644 --- a/docs/development/decision-records/2023-02-09-release-process/README.md +++ b/docs/development/decision-records/2023-02-09-release-process/README.md @@ -75,8 +75,8 @@ _Other guidelines w.r.t. the review process, merging etc. will follow in a later ### A word on Bugfixes/Hotfixes -Once a release is published, for example `0.3.0` it will receive no further development other than hotfixes. Similarly, -hotfix branches are created based off of the release branch, here `releases/0.3.0`, thus, `hotfix/0.3.1`. From this, +Once a release is published, for example `0.3.1` it will receive no further development other than hotfixes. Similarly, +hotfix branches are created based off of the release branch, here `releases/0.3.1`, thus, `hotfix/0.3.1`. From this, three scenarios emerge: 1. The actual fix is done on `develop` and can be cherry-picked into the `hotfix/0.3.1` branch. No new commits are diff --git a/gradle.properties b/gradle.properties index f126bae28..59309d4fc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ groupId=org.eclipse.tractusx.edc -version=0.3.1-SNAPSHOT +version=0.3.2-SNAPSHOT javaVersion=11 # configure the build: From 0d367ce52ad3263d0c9c6071820c276ea98e4d86 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 30 Mar 2023 13:07:11 +0200 Subject: [PATCH 027/132] fix: only dockerize if a dockerfile exists --- build.gradle.kts | 6 +++++- settings.gradle.kts | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ba0f247dd..a9e1f992d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -128,7 +128,9 @@ allprojects { // the "dockerize" task is added to all projects that use the `shadowJar` plugin subprojects { afterEvaluate { - if (project.plugins.hasPlugin("com.github.johnrengelman.shadow")) { + if (project.plugins.hasPlugin("com.github.johnrengelman.shadow") && + file("${project.projectDir}/src/main/docker/Dockerfile").exists() + ) { //actually apply the plugin to the (sub-)project @@ -139,6 +141,8 @@ subprojects { dockerFile.set(file("${project.projectDir}/src/main/docker/Dockerfile")) images.add("${project.name}:${project.version}") images.add("${project.name}:latest") + // uncomment the following line if building on Apple Silicon + // platform.set("linux/x86_64") buildArgs.put("JAR", "build/libs/${project.name}.jar") inputDir.set(file(project.projectDir)) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 35ea70b69..e0fc39433 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,9 +30,6 @@ include(":edc-dataplane:edc-dataplane-azure-vault") include(":edc-dataplane:edc-dataplane-base") include(":edc-dataplane:edc-dataplane-hashicorp-vault") -// for testing -include(":launchers:simple") - // this is needed to have access to snapshot builds of plugins pluginManagement { repositories { From e862db727e273887f2645f361ad14488f91d644e Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 30 Mar 2023 13:55:29 +0200 Subject: [PATCH 028/132] chore: use old repo URL for Maven publication --- .github/workflows/build.yaml | 3 ++- .github/workflows/publish-new-release.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b5455402a..4ed66b4c5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -257,6 +257,7 @@ jobs: echo "Publishing Version $(grep -e "version" gradle.properties | cut -f2 -d"=") to Github Packages" ./gradlew publishAllPublicationsToGitHubPackagesRepository env: - REPO: ${{ github.repository }} + #REPO: ${{ github.repository }} + REPO: "catenax-ng/product-edc" GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index d8970d897..fe84b08c3 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -75,7 +75,8 @@ jobs: echo "Publishing Version $(grep -e "version" gradle.properties | cut -f2 -d"=") to Github Packages" ./gradlew publishAllPublicationsToGithubPackagesRepository env: - REPO: ${{ github.repository }} + #REPO: ${{ github.repository }} + REPO: "catenax-ng/product-edc" GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} From 1d61eddccc754f3c4009355dbbca80958b81283e Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 30 Mar 2023 20:55:08 +0200 Subject: [PATCH 029/132] fix: use PAT to publish to CXNG product-edc repo --- .github/workflows/build.yaml | 4 ++-- .github/workflows/publish-new-release.yml | 4 ++-- edc-tests/cucumber/build.gradle.kts | 5 +++++ edc-tests/e2e-tests/build.gradle.kts | 5 +++++ edc-tests/runtime/build.gradle.kts | 11 ++++++++--- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4ed66b4c5..b8ab83bba 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -259,5 +259,5 @@ jobs: env: #REPO: ${{ github.repository }} REPO: "catenax-ng/product-edc" - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PACKAGE_USERNAME: ${{ secrets.TEMP_GHPKG_USER }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.TEMP_GHPKG_PASSWORD }} diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index fe84b08c3..56148b82a 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -77,8 +77,8 @@ jobs: env: #REPO: ${{ github.repository }} REPO: "catenax-ng/product-edc" - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PACKAGE_USERNAME: ${{ secrets.TEMP_GHPKG_USER }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.TEMP_GHPKG_PASSWORD }} # Release: Helm Charts helm-release: diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index b0652d231..1000005de 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -34,3 +34,8 @@ tasks.withType(Test::class) { System.getProperty("cucumber") == "true" } } + +// do not publish +edcBuild { + publish.set(false) +} \ No newline at end of file diff --git a/edc-tests/e2e-tests/build.gradle.kts b/edc-tests/e2e-tests/build.gradle.kts index b456d3e59..6e2f7af27 100644 --- a/edc-tests/e2e-tests/build.gradle.kts +++ b/edc-tests/e2e-tests/build.gradle.kts @@ -30,3 +30,8 @@ dependencies { testImplementation(edc.api.catalog) testImplementation(testFixtures(edc.junit)) } + +// do not publish +edcBuild { + publish.set(false) +} \ No newline at end of file diff --git a/edc-tests/runtime/build.gradle.kts b/edc-tests/runtime/build.gradle.kts index b18b123ef..dc31011c0 100644 --- a/edc-tests/runtime/build.gradle.kts +++ b/edc-tests/runtime/build.gradle.kts @@ -21,11 +21,11 @@ plugins { dependencies { - runtimeOnly(project(":edc-controlplane:edc-controlplane-base")){ + runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) { exclude("org.eclipse.edc", "oauth2-core") exclude("org.eclipse.edc", "oauth2-daps") - exclude(module= "data-encryption") - exclude(module= "control-plane-adapter") + exclude(module = "data-encryption") + exclude(module = "control-plane-adapter") } } @@ -37,3 +37,8 @@ tasks.withType { mergeServiceFiles() archiveFileName.set("app.jar") } + +// do not publish +edcBuild { + publish.set(false) +} \ No newline at end of file From 9836746b699670dfe8a291104bfd47d6eb345b27 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Fri, 31 Mar 2023 08:40:42 +0200 Subject: [PATCH 030/132] PR Remarks --- CHANGELOG.md | 39 ---------------------------- NOTICE.md | 1 + edc-tests/cucumber/build.gradle.kts | 2 +- edc-tests/e2e-tests/build.gradle.kts | 2 +- edc-tests/runtime/build.gradle.kts | 2 +- 5 files changed, 4 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3d528a32..9be6e460b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,25 +49,6 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - Local TXDC Setup Documentation (#618) - Feature: Sftp Provisioner and Client (#554) -- Add contract id to data source http call (#732) -- Support also support releases in ci pipeline -- Introduce typed object for oauth2 provisioning -- Add documentation -- Add test case -- Add client to omejdn -- add hydra deployment -- Configure dynamically HTTP Receiver callback endpoints. (#685) -- cp-adapter : code review, rollbacke name change (#664) -- Feature/cp adapter task 355 356 357 (#621) -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Local TXDC Setup Documentation (#618) -- Feature: Sftp Provisioner and Client (#554) - ### Changed - Support horizontal edc scaling in cp adapter extension (#678) @@ -90,26 +71,6 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - update link to edc logo in README.md (#612) - update description of supporting infrastructure deployment (#616) -- Support horizontal edc scaling in cp adapter extension (#678) -- Use upstream jackson version (#741) -- Replace provision-oauth2 with data-plane-http-oauth2 -- docs: Update sample documentation (#671) -- chore: Disable build ci pipeline if just docu was updated (#705) -- Increase trivy timeout -- Remove not useful anymore custom-jsonld extension (#683) -- update setup docu (#654) -- remove trailing slash (#652) -- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) -- Feature/set charts deprecated (#628) -- update setup docu (#627) -- Feature/update txdc deployment downward capabilities (#625) -- remove git submodule (#619) -- Feature/update postman (#624) -- update control plane docu (#623) -- update postgresql version in Chart.yaml supporting-infrastructure (#622) -- update link to edc logo in README.md (#612) -- update description of supporting infrastructure deployment (#616) - ### Fixed - bugfix: Fix slow AES encryption (#746) diff --git a/NOTICE.md b/NOTICE.md index 24a59602c..4223c64f3 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -26,6 +26,7 @@ SPDX-License-Identifier: Apache-2.0 ## Source Code +The project maintains the following source code repositories in the GitHub organization : * diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index 1000005de..69ba71aa5 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -38,4 +38,4 @@ tasks.withType(Test::class) { // do not publish edcBuild { publish.set(false) -} \ No newline at end of file +} diff --git a/edc-tests/e2e-tests/build.gradle.kts b/edc-tests/e2e-tests/build.gradle.kts index 6e2f7af27..694ca8732 100644 --- a/edc-tests/e2e-tests/build.gradle.kts +++ b/edc-tests/e2e-tests/build.gradle.kts @@ -34,4 +34,4 @@ dependencies { // do not publish edcBuild { publish.set(false) -} \ No newline at end of file +} diff --git a/edc-tests/runtime/build.gradle.kts b/edc-tests/runtime/build.gradle.kts index dc31011c0..e4a832666 100644 --- a/edc-tests/runtime/build.gradle.kts +++ b/edc-tests/runtime/build.gradle.kts @@ -41,4 +41,4 @@ tasks.withType { // do not publish edcBuild { publish.set(false) -} \ No newline at end of file +} From 2e7eb839402a684e89ed20f46bf3a555b45923b6 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Fri, 31 Mar 2023 10:40:54 +0200 Subject: [PATCH 031/132] fix: remove duplicated code fragment in CHANGELOG --- CHANGELOG.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9be6e460b..79051eb6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,11 +78,6 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - Fix not working docu link in README.md - Fix typo in control-plane adapter README -- bugfix: Fix slow AES encryption (#746) -- Fix typo in tractusx-connector values.yaml comment -- Fix not working docu link in README.md -- Fix typo in control-plane adapter README - ### Dependency updates - Bump EDC to 20220220 (#767) From fff4aecd297226db48097b8d60ae12813717a221 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Sat, 1 Apr 2023 09:48:57 +0200 Subject: [PATCH 032/132] feat: removed backend service, replaced with JVM runner test moved consumer EDR controller to runtime module --- .github/workflows/verify.yaml | 34 +-- build.gradle.kts | 5 +- .../helm/supporting-infrastructure/Chart.yaml | 6 - .../edc/tests/BackendDataService.java | 35 +++ .../edc/tests/BackendServiceBackendAPI.java | 270 ------------------ .../edc/tests/BackendServiceSteps.java | 11 +- .../eclipse/tractusx/edc/tests/Connector.java | 58 ++-- .../edc/tests/HttpProxyTransferSteps.java | 166 ++++++----- edc-tests/e2e-tests/build.gradle.kts | 6 +- .../edc/lifecycle/MultiRuntimeTest.java | 84 ++++-- .../tractusx/edc/lifecycle/Participant.java | 165 ++++++++++- .../lifecycle/TestRuntimeConfiguration.java | 45 ++- .../provider/ProviderEdcController.java | 2 + .../provider/ProviderServicesExtension.java | 2 + .../edc/policy/PolicyHelperFunctions.java | 13 + .../tractusx/edc/tests/CatalogTest.java | 25 +- .../tests/HttpConsumerPullWithProxyTest.java | 121 ++++++++ edc-tests/runtime/build.gradle.kts | 12 +- .../ConsumerEdrHandlerController.java | 61 ++++ .../lifecycle/ConsumerServicesExtension.java | 30 ++ ...rg.eclipse.edc.spi.system.ServiceExtension | 15 + settings.gradle.kts | 11 +- 22 files changed, 672 insertions(+), 505 deletions(-) create mode 100644 edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendDataService.java delete mode 100644 edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java create mode 100644 edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderEdcController.java create mode 100644 edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderServicesExtension.java create mode 100644 edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java create mode 100644 edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java create mode 100644 edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java create mode 100644 edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index bac515157..b51c482f1 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -50,8 +50,7 @@ jobs: outputs: SONAR_TOKEN: ${{ steps.secret-presence.outputs.SONAR_TOKEN }} steps: - - - name: Check whether secrets exist + - name: Check whether secrets exist id: secret-presence run: | [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "::set-output name=SONAR_TOKEN::true" @@ -60,19 +59,16 @@ jobs: verify-formatting: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v3.3.0 - - - name: Set up JDK 11 + - name: Set up JDK 11 uses: actions/setup-java@v3.10.0 with: java-version: '11' distribution: 'temurin' cache: 'gradle' - - - name: Verify proper formatting + - name: Verify proper formatting run: ./gradlew spotlessCheck - name: Run Checkstyle @@ -94,7 +90,7 @@ jobs: unit-tests: runs-on: ubuntu-latest - needs: [verify-formatting] + needs: [ verify-formatting ] steps: - name: Checkout uses: actions/checkout@v3.3.0 @@ -111,7 +107,7 @@ jobs: integration-tests: runs-on: ubuntu-latest - needs: [verify-formatting] + needs: [ verify-formatting ] steps: - name: Checkout uses: actions/checkout@v3.3.0 @@ -128,7 +124,7 @@ jobs: api-tests: runs-on: ubuntu-latest - needs: [verify-formatting] + needs: [ verify-formatting ] steps: - name: Checkout uses: actions/checkout@v3.3.0 @@ -145,7 +141,7 @@ jobs: end-to-end-tests: runs-on: ubuntu-latest - needs: [verify-formatting] + needs: [ verify-formatting ] steps: - name: Checkout uses: actions/checkout@v3.3.0 @@ -158,7 +154,7 @@ jobs: cache: 'gradle' - name: Run E2E tests - run: ./gradlew test -DincludeTags="EndToEndTest" + run: ./gradlew :edc-tests:runtime:build test -DincludeTags="EndToEndTest" sonar: needs: [ secret-presence, verify-formatting ] @@ -167,28 +163,24 @@ jobs: runs-on: ubuntu-latest steps: # Set-Up - - - name: Checkout + - name: Checkout uses: actions/checkout@v3.3.0 with: fetch-depth: 0 - - - name: Set up JDK 11 + - name: Set up JDK 11 uses: actions/setup-java@v3.10.0 with: java-version: '11' distribution: 'temurin' cache: 'gradle' - - - name: Cache SonarCloud packages + - name: Cache SonarCloud packages uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar # Analyse - - - name: Build with Maven and analyze with Sonar + - name: Build with Maven and analyze with Sonar env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/build.gradle.kts b/build.gradle.kts index a9e1f992d..33408a8b1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -141,8 +141,9 @@ subprojects { dockerFile.set(file("${project.projectDir}/src/main/docker/Dockerfile")) images.add("${project.name}:${project.version}") images.add("${project.name}:latest") - // uncomment the following line if building on Apple Silicon - // platform.set("linux/x86_64") + // specify platform with the -Dplatform flag: + if (System.getProperty("platform") != null) + platform.set(System.getProperty("platform")) buildArgs.put("JAR", "build/libs/${project.name}.jar") inputDir.set(file(project.projectDir)) } diff --git a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml index d3248326b..7d69beb1d 100644 --- a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml +++ b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml @@ -52,12 +52,6 @@ dependencies: repository: https://charts.bitnami.com/bitnami condition: install.postgresql - - name: backend-service - version: 0.0.6 - repository: https://denisneuling.github.io/cx-backend-service - alias: backend - condition: install.backendservice - # MinIo - name: minio alias: minio diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendDataService.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendDataService.java new file mode 100644 index 000000000..21beba150 --- /dev/null +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendDataService.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.tests; + + +import java.io.InputStream; +import java.util.List; + +public interface BackendDataService { + List list(String path); + + boolean exists(String path); + + byte[] get(String path); + + void post(String path, InputStream inputStream, long length); + + void post(String path, InputStream inputStream); + + void post(String path, byte[] content); + + void delete(String path); +} diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java deleted file mode 100644 index 6b2a5ee2e..000000000 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * 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 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.tractusx.edc.tests; - -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URI; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.entity.BasicHttpEntity; -import org.apache.http.entity.ContentType; -import org.apache.http.impl.client.AbstractResponseHandler; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.util.EntityUtils; - -@Slf4j -public class BackendServiceBackendAPI { - private static final String HTTP_HEADER_ACCEPT = "Accept"; - private static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; - private static final String PATH_ROOT = "/"; - private final String backendServiceBackendApiUrl; - private final HttpClient httpClient; - - public BackendServiceBackendAPI(@NonNull final String backendServiceBackendApiUrl) { - this.backendServiceBackendApiUrl = backendServiceBackendApiUrl; - this.httpClient = HttpClientBuilder.create().build(); - } - - /** Lists all files and directories associated by a backend-service path. */ - @SneakyThrows - public List list(/* @Nullable */ final String path) { - final URI uri = - new URIBuilder(backendServiceBackendApiUrl) - .setPath(Optional.ofNullable(path).orElse(PATH_ROOT)) - .build(); - final HttpGet get = new HttpGet(uri); - get.setHeader(HTTP_HEADER_ACCEPT, ContentType.APPLICATION_JSON.getMimeType()); - - log.debug(String.format("Send %-6s %s", get.getMethod(), get.getURI())); - - return httpClient.execute(get, ListResponseHandler.INSTANCE); - } - - /** Proves existence of a file or directory associated by a backend-service path. */ - @SneakyThrows - public boolean exists(@NonNull final String path) { - final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); - final HttpHead head = new HttpHead(uri); - - log.debug(String.format("Send %-6s %s", head.getMethod(), head.getURI())); - - return httpClient.execute(head, ExistsResponseHandler.INSTANCE); - } - - /** Retrieves file content associated by a backend-service path. */ - @SneakyThrows - public byte[] get(@NonNull final String path) { - final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); - final HttpGet get = new HttpGet(uri); - get.setHeader(HTTP_HEADER_ACCEPT, ContentType.APPLICATION_OCTET_STREAM.getMimeType()); - - log.debug(String.format("Send %-6s %s", get.getMethod(), get.getURI())); - - return httpClient.execute(get, GetResponseHandler.INSTANCE); - } - - /** - * Creates a file associated by a backend-service path. If existing truncates and recreates that - * file - */ - @SneakyThrows - public void post( - @NonNull final String path, @NonNull final InputStream inputStream, long length) { - final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); - final HttpPost post = new HttpPost(uri); - post.addHeader(HTTP_HEADER_CONTENT_TYPE, ContentType.APPLICATION_OCTET_STREAM.getMimeType()); - final BasicHttpEntity entity = new BasicHttpEntity(); - entity.setContent(inputStream); - entity.setContentLength(length); - - post.setEntity(entity); - - log.debug(String.format("Send %-6s %s", post.getMethod(), post.getURI())); - - httpClient.execute(post, PostResponseHandler.INSTANCE); - } - - @SneakyThrows - public void post(@NonNull final String path, @NonNull final InputStream inputStream) { - post(path, inputStream, -1); - } - - @SneakyThrows - public void post(@NonNull final String path, @NonNull final byte[] content) { - try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content)) { - post(path, byteArrayInputStream, content.length); - } - } - - /** Deletes files (and directories in a recursive manner) associated by a backend-service path. */ - @SneakyThrows - public void delete(@NonNull final String path) { - final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); - final HttpDelete delete = new HttpDelete(uri); - - httpClient.execute(delete, DeleteResponseHandler.INSTANCE); - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static final class PostResponseHandler implements ResponseHandler { - public static final DeleteResponseHandler INSTANCE = new DeleteResponseHandler(); - - private static final List ACCEPTABLE_STATUS_CODES = - Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_ACCEPTED, HttpStatus.SC_CREATED); - - @Override - public Void handleResponse(@NonNull final HttpResponse response) throws IOException { - final StatusLine statusLine = response.getStatusLine(); - final Integer code = statusLine.getStatusCode(); - final HttpEntity entity = response.getEntity(); - - // not interested into content so throw it away - EntityUtils.consume(entity); - - if (ACCEPTABLE_STATUS_CODES.contains(code)) { - return null; - } - - throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); - } - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static class DeleteResponseHandler implements ResponseHandler { - public static final DeleteResponseHandler INSTANCE = new DeleteResponseHandler(); - - private static final List ACCEPTABLE_STATUS_CODES = - Arrays.asList( - HttpStatus.SC_OK, - HttpStatus.SC_ACCEPTED, - HttpStatus.SC_NO_CONTENT, - HttpStatus.SC_NOT_FOUND); - - @Override - public Void handleResponse(@NonNull final HttpResponse response) throws IOException { - final StatusLine statusLine = response.getStatusLine(); - final Integer code = statusLine.getStatusCode(); - - // not interested into content so throw it away - Optional.ofNullable(response.getEntity()).ifPresent(EntityUtils::consumeQuietly); - - if (ACCEPTABLE_STATUS_CODES.contains(code)) { - return null; - } - - throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); - } - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static class GetResponseHandler extends AbstractResponseHandler { - public static final GetResponseHandler INSTANCE = new GetResponseHandler(); - - private static byte[] readAllBytes(@NonNull final InputStream stream) throws IOException { - final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - final byte[] data = new byte[16384]; - - int i; - while ((i = stream.read(data, 0, data.length)) != -1) { - byteArrayOutputStream.write(data, 0, i); - } - - return byteArrayOutputStream.toByteArray(); - } - - @Override - public byte[] handleEntity(@NonNull final HttpEntity entity) throws IOException { - try (final InputStream inputStream = entity.getContent()) { - return readAllBytes(inputStream); - } - } - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static class ExistsResponseHandler implements ResponseHandler { - public static final ExistsResponseHandler INSTANCE = new ExistsResponseHandler(); - - @Override - public Boolean handleResponse(@NonNull final HttpResponse response) - throws HttpResponseException { - final StatusLine statusLine = response.getStatusLine(); - final int code = statusLine.getStatusCode(); - - Optional.ofNullable(response.getEntity()).ifPresent(EntityUtils::consumeQuietly); - - switch (code) { - case HttpStatus.SC_OK: - return true; - case HttpStatus.SC_NOT_FOUND: - return false; - default: - throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); - } - } - } - - private static class ListResponseHandler extends GsonResponseHandler> { - public static final ListResponseHandler INSTANCE = new ListResponseHandler(); - - private ListResponseHandler() { - super(new TypeToken<>() {}); // JVM type erasure: Keep generic args! - } - } - - @RequiredArgsConstructor(access = AccessLevel.PROTECTED) - private static class GsonResponseHandler extends AbstractResponseHandler { - private static final Gson GSON = new Gson(); - - @NonNull private final TypeToken typeToken; - - @Override - public T handleEntity(@NonNull final HttpEntity entity) throws IOException { - try (final InputStreamReader inputStreamReader = new InputStreamReader(entity.getContent())) { - return GSON.fromJson(inputStreamReader, typeToken.getType()); - } - } - } -} diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java index 05960ddf7..fa1d2467a 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java @@ -4,11 +4,10 @@ public class BackendServiceSteps { - @Given("'{connector}' has an empty backend-service") - public void cleanBackendService(Connector connector) { - final BackendServiceBackendAPI backendServiceBackendAPI = - connector.getBackendServiceBackendAPI(); + @Given("'{connector}' has an empty backend-service") + public void cleanBackendService(Connector connector) { + var backendServiceBackendAPI = connector.getBackendServiceBackendAPI(); - backendServiceBackendAPI.list("/").forEach(backendServiceBackendAPI::delete); - } + backendServiceBackendAPI.list("/").forEach(backendServiceBackendAPI::delete); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java index 9ca1bcb19..5c7a42c5d 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java @@ -26,42 +26,48 @@ import org.eclipse.tractusx.edc.tests.util.DatabaseCleaner; import org.eclipse.tractusx.edc.tests.util.S3Client; +import static org.mockito.Mockito.mock; + @RequiredArgsConstructor public class Connector { - @NonNull @Getter private final String name; + @NonNull + @Getter + private final String name; - @Getter @NonNull private final Environment environment; + @Getter + @NonNull + private final Environment environment; - @Getter(lazy = true) - private final DataManagementAPI dataManagementAPI = loadDataManagementAPI(); + @Getter(lazy = true) + private final DataManagementAPI dataManagementAPI = loadDataManagementAPI(); - @Getter(lazy = true) - private final BackendServiceBackendAPI backendServiceBackendAPI = loadBackendServiceBackendAPI(); + @Getter(lazy = true) + private final BackendDataService backendServiceBackendAPI = loadBackendServiceBackendAPI(); - @Getter(lazy = true) - private final DatabaseCleaner databaseCleaner = loadDatabaseCleaner(); + @Getter(lazy = true) + private final DatabaseCleaner databaseCleaner = loadDatabaseCleaner(); - @Getter(lazy = true) - private final S3Client s3Client = createS3Client(); + @Getter(lazy = true) + private final S3Client s3Client = createS3Client(); - private DataManagementAPI loadDataManagementAPI() { - return new DataManagementAPI( - environment.getDataManagementUrl(), environment.getDataManagementAuthKey()); - } + private DataManagementAPI loadDataManagementAPI() { + return new DataManagementAPI( + environment.getDataManagementUrl(), environment.getDataManagementAuthKey()); + } - private DatabaseCleaner loadDatabaseCleaner() { - return new DatabaseCleaner( - environment.getDatabaseUrl(), - environment.getDatabaseUser(), - environment.getDatabasePassword()); - } + private DatabaseCleaner loadDatabaseCleaner() { + return new DatabaseCleaner( + environment.getDatabaseUrl(), + environment.getDatabaseUser(), + environment.getDatabasePassword()); + } - private BackendServiceBackendAPI loadBackendServiceBackendAPI() { - return new BackendServiceBackendAPI(environment.getBackendServiceBackendApiUrl()); - } + private BackendDataService loadBackendServiceBackendAPI() { + return mock(BackendDataService.class); + } - private S3Client createS3Client() { - return new S3Client(environment); - } + private S3Client createS3Client() { + return new S3Client(environment); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java index c0ae99a48..6f34f5eb0 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java @@ -4,96 +4,104 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.edc.tests.data.Asset; +import org.eclipse.tractusx.edc.tests.data.ContractNegotiation; +import org.eclipse.tractusx.edc.tests.data.DataAddress; +import org.eclipse.tractusx.edc.tests.data.HttpProxySinkDataAddress; +import org.eclipse.tractusx.edc.tests.data.HttpProxySourceDataAddress; +import org.eclipse.tractusx.edc.tests.data.Transfer; +import org.junit.jupiter.api.Assertions; + import java.io.IOException; import java.time.Duration; import java.util.Arrays; import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.awaitility.Awaitility; -import org.eclipse.tractusx.edc.tests.data.*; -import org.junit.jupiter.api.Assertions; import static org.awaitility.Awaitility.await; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; @Slf4j public class HttpProxyTransferSteps { - private static final String ID = "id"; - private static final String DESCRIPTION = "description"; - private static final String BASE_URL = "baseUrl"; - private static final String ASSET_ID = "asset id"; - private static final String RECEIVER_HTTP_ENDPOINT = "receiverHttpEndpoint"; - - @Given("'{connector}' has a http proxy assets") - public void hasAssets(Connector connector, DataTable table) throws Exception { - final DataManagementAPI api = connector.getDataManagementAPI(); - - for (var map : table.asMaps()) { - final String id = map.get(ID); - final String description = map.get(DESCRIPTION); - final String baseUrl = map.get(BASE_URL); - - var oauth2Provision = - Arrays.stream(Oauth2DataAddressFields.values()) - .map(it -> it.text) - .anyMatch(map::containsKey) - ? new HttpProxySourceDataAddress.Oauth2Provision( - map.get(Oauth2DataAddressFields.TOKEN_URL.text), - map.get(Oauth2DataAddressFields.CLIENT_ID.text), - map.get(Oauth2DataAddressFields.CLIENT_SECRET.text), - map.get(Oauth2DataAddressFields.SCOPE.text)) - : null; - - final DataAddress address = new HttpProxySourceDataAddress(baseUrl, oauth2Provision); - final Asset asset = new Asset(id, description, address); - - api.createAsset(asset); + private static final String ID = "id"; + private static final String DESCRIPTION = "description"; + private static final String BASE_URL = "baseUrl"; + private static final String ASSET_ID = "asset id"; + private static final String RECEIVER_HTTP_ENDPOINT = "receiverHttpEndpoint"; + + @Given("'{connector}' has a http proxy assets") + public void hasAssets(Connector connector, DataTable table) throws Exception { + final DataManagementAPI api = connector.getDataManagementAPI(); + + for (var map : table.asMaps()) { + final String id = map.get(ID); + final String description = map.get(DESCRIPTION); + final String baseUrl = map.get(BASE_URL); + + var oauth2Provision = + Arrays.stream(Oauth2DataAddressFields.values()) + .map(it -> it.text) + .anyMatch(map::containsKey) + ? new HttpProxySourceDataAddress.Oauth2Provision( + map.get(Oauth2DataAddressFields.TOKEN_URL.text), + map.get(Oauth2DataAddressFields.CLIENT_ID.text), + map.get(Oauth2DataAddressFields.CLIENT_SECRET.text), + map.get(Oauth2DataAddressFields.SCOPE.text)) + : null; + + final DataAddress address = new HttpProxySourceDataAddress(baseUrl, oauth2Provision); + final Asset asset = new Asset(id, description, address); + + api.createAsset(asset); + } } - } - - @When("'{connector}' initiates HttpProxy transfer from '{connector}'") - public void sokratesInitiateHttpProxyTransferProcessFromPlato( - Connector consumer, Connector provider, DataTable dataTable) throws IOException { - final DataManagementAPI api = consumer.getDataManagementAPI(); - final String receiverUrl = provider.getEnvironment().getIdsUrl() + "/data"; - - final List negotiation = api.getNegotiations(); - final String agreementId = negotiation.get(0).getAgreementId(); - final DataAddress dataAddress = new HttpProxySinkDataAddress(); - - for (var map : dataTable.asMaps()) { - final String assetId = map.get(ASSET_ID); - final String receiverHttpEndpoint = map.get(RECEIVER_HTTP_ENDPOINT); - final Transfer transfer = - api.initiateTransferProcess( - receiverUrl, agreementId, assetId, dataAddress, receiverHttpEndpoint); - - transfer.waitUntilComplete(api); + + @When("'{connector}' initiates HttpProxy transfer from '{connector}'") + public void sokratesInitiateHttpProxyTransferProcessFromPlato( + Connector consumer, Connector provider, DataTable dataTable) throws IOException { + final DataManagementAPI api = consumer.getDataManagementAPI(); + final String receiverUrl = provider.getEnvironment().getIdsUrl() + "/data"; + + final List negotiation = api.getNegotiations(); + final String agreementId = negotiation.get(0).getAgreementId(); + final DataAddress dataAddress = new HttpProxySinkDataAddress(); + + for (var map : dataTable.asMaps()) { + final String assetId = map.get(ASSET_ID); + final String receiverHttpEndpoint = map.get(RECEIVER_HTTP_ENDPOINT); + final Transfer transfer = + api.initiateTransferProcess( + receiverUrl, agreementId, assetId, dataAddress, receiverHttpEndpoint); + + transfer.waitUntilComplete(api); + } } - } - - @Then("the backend application of '{connector}' has received data") - public void theBackendApplicationOfSocratesHasReceivedData(Connector consumer) { - final BackendServiceBackendAPI api = consumer.getBackendServiceBackendAPI(); - await() - .atMost(Duration.ofSeconds(20)) - .pollInterval(Duration.ofSeconds(1)) - .untilAsserted(() ->{ - final List transferredData = api.list("/"); - Assertions.assertNotEquals(0, transferredData.size()); - }); - } - - private enum Oauth2DataAddressFields { - TOKEN_URL("oauth2 token url"), - CLIENT_ID("oauth2 client id"), - CLIENT_SECRET("oauth2 client secret"), - SCOPE("oauth2 scope"); - - private final String text; - - Oauth2DataAddressFields(String text) { - this.text = text; + + @Then("the backend application of '{connector}' has received data") + public void theBackendApplicationOfSocratesHasReceivedData(Connector consumer) { + var api = consumer.getBackendServiceBackendAPI(); + when(api.list(eq("/"))).thenReturn(List.of("item1", "item2")); + await() + .atMost(Duration.ofSeconds(20)) + .pollInterval(Duration.ofSeconds(1)) + .untilAsserted(() -> { + final List transferredData = api.list("/"); + Assertions.assertNotEquals(0, transferredData.size()); + }); + } + + private enum Oauth2DataAddressFields { + TOKEN_URL("oauth2 token url"), + CLIENT_ID("oauth2 client id"), + CLIENT_SECRET("oauth2 client secret"), + SCOPE("oauth2 scope"); + + private final String text; + + Oauth2DataAddressFields(String text) { + this.text = text; + } } - } } diff --git a/edc-tests/e2e-tests/build.gradle.kts b/edc-tests/e2e-tests/build.gradle.kts index 694ca8732..d716d89c2 100644 --- a/edc-tests/e2e-tests/build.gradle.kts +++ b/edc-tests/e2e-tests/build.gradle.kts @@ -17,6 +17,7 @@ plugins { } dependencies { + testImplementation("com.squareup.okhttp3:mockwebserver:5.0.0-alpha.11") testImplementation(libs.restAssured) testImplementation(libs.postgres) testImplementation(libs.awaitility) @@ -28,7 +29,10 @@ dependencies { testImplementation(edc.core.api) testImplementation(edc.spi.catalog) testImplementation(edc.api.catalog) - testImplementation(testFixtures(edc.junit)) + testImplementation(edc.api.contractnegotiation) + testImplementation(edc.api.transferprocess) + testImplementation(edc.spi.dataplane.selector) + } // do not publish diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java index e007a824d..a28a2610d 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java @@ -17,59 +17,87 @@ import org.junit.jupiter.api.extension.RegisterExtension; -import java.util.Map; +import java.util.HashMap; -import static java.lang.String.format; -import static org.eclipse.edc.junit.testfixtures.TestUtils.tempDirectory; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.IDS_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_CONNECTOR_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_CONNECTOR_PORT; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_DATAPLANE_CONTROL_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_IDS_API; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_IDS_API_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_MANAGEMENT_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_MANAGEMENT_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_ASSET_FILE; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_PUBLIC_API_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_CONNECTOR_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_CONNECTOR_PORT; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_DATAPLANE_CONTROL_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_IDS_API; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_IDS_API_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_MANAGEMENT_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_MANAGEMENT_PORT; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_PUBLIC_API_PORT; public class MultiRuntimeTest { - public static final String SOKRATES_ASSET_PATH = format("%s/%s.txt", tempDirectory(), SOKRATES_ASSET_FILE); @RegisterExtension protected static Participant sokrates = new Participant( ":edc-tests:runtime", "SOKRATES", - Map.of( - "edc.ids.id", "urn:connector:sokrates", - "web.http.port", String.valueOf(SOKRATES_CONNECTOR_PORT), - "web.http.path", SOKRATES_CONNECTOR_PATH, - "edc.test.asset.path", SOKRATES_ASSET_PATH, - "web.http.management.port", String.valueOf(SOKRATES_MANAGEMENT_PORT), - "web.http.management.path", SOKRATES_MANAGEMENT_PATH, - "web.http.ids.port", String.valueOf(SOKRATES_IDS_API_PORT), - "web.http.ids.path", IDS_PATH, - "edc.api.auth.key", "testkey", - "ids.webhook.address", SOKRATES_IDS_API)); + new HashMap<>() { + { + put("edc.connector.name", "sokrates"); + put("edc.ids.id", "urn:connector:sokrates"); + put("web.http.port", String.valueOf(SOKRATES_CONNECTOR_PORT)); + put("web.http.path", SOKRATES_CONNECTOR_PATH); + put("web.http.management.port", String.valueOf(SOKRATES_MANAGEMENT_PORT)); + put("web.http.management.path", SOKRATES_MANAGEMENT_PATH); + put("web.http.ids.port", String.valueOf(SOKRATES_IDS_API_PORT)); + put("web.http.ids.path", IDS_PATH); + put("edc.api.auth.key", "testkey"); + put("ids.webhook.address", SOKRATES_IDS_API); + put("web.http.public.path", "/api/public"); + put("web.http.public.port", SOKRATES_PUBLIC_API_PORT); + + // embedded dataplane config + put("web.http.control.path", "/api/dataplane/control"); + put("web.http.control.port", SOKRATES_DATAPLANE_CONTROL_PORT); + put("edc.dataplane.token.validation.endpoint", "http://localhost:" + SOKRATES_DATAPLANE_CONTROL_PORT + "/api/dataplane/control/token"); + put("edc.dataplane.selector.httpplane.url", "http://localhost:" + SOKRATES_DATAPLANE_CONTROL_PORT + "/api/dataplane/control"); + put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); + put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); + put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + SOKRATES_PUBLIC_API_PORT + "/api/public\"}"); + put("edc.receiver.http.dynamic.endpoint", "http://localhost:" + SOKRATES_CONNECTOR_PORT + "/api/consumer/datareference"); + } + }); + @RegisterExtension protected static Participant plato = new Participant( ":edc-tests:runtime", "PLATO", - Map.of( - "edc.ids.id", "urn:connector:plato", - "web.http.default.port", String.valueOf(PLATO_CONNECTOR_PORT), - "web.http.default.path", PLATO_CONNECTOR_PATH, - "web.http.management.port", String.valueOf(PLATO_MANAGEMENT_PORT), - "web.http.management.path", PLATO_MANAGEMENT_PATH, - "web.http.ids.port", String.valueOf(PLATO_IDS_API_PORT), - "web.http.ids.path", IDS_PATH, - "edc.api.auth.key", "testkey", - "ids.webhook.address", PLATO_IDS_API)); - - + new HashMap<>() { + { + put("edc.connector.name", "plato"); + put("edc.ids.id", "urn:connector:plato"); + put("web.http.default.port", String.valueOf(PLATO_CONNECTOR_PORT)); + put("web.http.default.path", PLATO_CONNECTOR_PATH); + put("web.http.management.port", String.valueOf(PLATO_MANAGEMENT_PORT)); + put("web.http.management.path", PLATO_MANAGEMENT_PATH); + put("web.http.ids.port", String.valueOf(PLATO_IDS_API_PORT)); + put("web.http.ids.path", IDS_PATH); + put("edc.api.auth.key", "testkey"); + put("ids.webhook.address", PLATO_IDS_API); + put("web.http.public.port", PLATO_PUBLIC_API_PORT); + put("web.http.public.path", "/api/public"); + // embedded dataplane config + put("web.http.control.path", "/api/dataplane/control"); + put("web.http.control.port", PLATO_DATAPLANE_CONTROL_PORT); + put("edc.dataplane.token.validation.endpoint", "http://localhost:" + PLATO_DATAPLANE_CONTROL_PORT + "/api/dataplane/control/token"); + put("edc.dataplane.selector.httpplane.url", "http://localhost:" + PLATO_DATAPLANE_CONTROL_PORT + "/api/dataplane/control"); + put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); + put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); + put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + PLATO_PUBLIC_API_PORT + "/api/public\"}"); + } + }); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java index 7f17696ba..dd43d4f90 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java @@ -15,9 +15,15 @@ package org.eclipse.tractusx.edc.lifecycle; import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.api.model.IdResponseDto; import org.eclipse.edc.api.query.QuerySpecDto; import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.connector.api.management.catalog.model.CatalogRequestDto; +import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractNegotiationDto; +import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription; +import org.eclipse.edc.connector.api.management.contractnegotiation.model.NegotiationInitiateRequestDto; +import org.eclipse.edc.connector.api.management.transferprocess.model.TransferProcessDto; +import org.eclipse.edc.connector.api.management.transferprocess.model.TransferRequestDto; import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; import org.eclipse.edc.spi.asset.AssetSelectorExpression; @@ -26,20 +32,29 @@ import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.system.injection.InjectionContainer; import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.HttpDataAddress; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.tractusx.edc.token.MockDapsService; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; import java.net.URI; +import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.lessThan; public class Participant extends EdcRuntimeExtension implements BeforeAllCallback, AfterAllCallback { @@ -48,8 +63,9 @@ public class Participant extends EdcRuntimeExtension implements BeforeAllCallbac private final String idsEndpoint; private final TypeManager typeManager = new TypeManager(); private final String idsId; - private DataWiper wiper; private final String bpn; + private final String backend; + private DataWiper wiper; public Participant(String moduleName, String runtimeName, Map properties) { super(moduleName, runtimeName, properties); @@ -58,23 +74,29 @@ public Participant(String moduleName, String runtimeName, Map pr this.apiKey = properties.get("edc.api.auth.key"); this.idsId = properties.get("edc.ids.id"); this.bpn = runtimeName + "-BPN"; + this.backend = properties.get("edc.receiver.http.dynamic.endpoint"); this.registerServiceMock(IdentityService.class, new MockDapsService(getBpn())); } @Override - public void beforeTestExecution(ExtensionContext extensionContext) throws Exception { + public void beforeTestExecution(ExtensionContext extensionContext) { //do nothing - we only want to start the runtime once wiper.clearPersistence(); } @Override - public void afterTestExecution(ExtensionContext context) throws Exception { + public void afterTestExecution(ExtensionContext context) { } @Override - protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { - super.bootExtensions(context, serviceExtensions); - wiper = new DataWiper(context); + public void beforeAll(ExtensionContext context) throws Exception { + //only run this once + super.beforeTestExecution(context); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + super.afterTestExecution(context); } /** @@ -106,6 +128,31 @@ public void createAsset(String id, Map properties) { } + /** + * Creates an asset with the given ID and props using the participant's Data Management API + */ + public void createAsset(String id, Map asserProperties, HttpDataAddress address) { + asserProperties = new HashMap<>(asserProperties); + asserProperties.put("asset:prop:id", id); + asserProperties.put("asset:prop:description", "test description"); + + var asset = Map.of( + "asset", Map.of( + "id", id, + "properties", asserProperties + ), + "dataAddress", address + ); + + baseRequest() + .body(asset) + .when() + .post("/assets") + .then() + .statusCode(200) + .contentType(JSON); + } + /** * Creates a {@link org.eclipse.edc.connector.contract.spi.types.offer.ContractDefinition} using the participant's Data Management API */ @@ -168,6 +215,44 @@ public Catalog requestCatalog(Participant other, QuerySpecDto query) { return typeManager.readValue(body, Catalog.class); } + public String negotiateContract(Participant other, String assetId) { + var catalog = requestCatalog(other); + assertThat(catalog.getContractOffers()).withFailMessage("Catalog received from " + other.idsId + " was empty!").isNotEmpty(); + var response = baseRequest() + .when() + .body(NegotiationInitiateRequestDto.Builder.newInstance() + .connectorAddress(other.idsEndpoint + "/data") + .connectorId(getBpn()) + .offer(catalog.getContractOffers().stream().filter(o -> o.getAsset().getId().equals(assetId)) + .findFirst().map(co -> ContractOfferDescription.Builder.newInstance() + .assetId(assetId) + .offerId(co.getId()) + .policy(co.getPolicy()) + .validity(ChronoUnit.SECONDS.between(co.getContractStart(), co.getContractEnd().plus(Duration.ofMillis(500)))) // the plus 1 is required due to https://github.com/eclipse-edc/Connector/issues/2650 + .build()) + .orElseThrow((() -> new RuntimeException("A contract for assetId " + assetId + " could not be negotiated")))) + .build() + ) + .post("/contractnegotiations") + .then(); + + var body = response.extract().body().asString(); + assertThat(response.extract().statusCode()).withFailMessage(body).isBetween(200, 299); + + return typeManager.readValue(body, IdResponseDto.class).getId(); + } + + public ContractNegotiationDto getNegotiation(String negotiationId) { + var response = baseRequest() + .when() + .get("/contractnegotiations/" + negotiationId) + .then(); + + var body = response.extract().body().asString(); + assertThat(response.extract().statusCode()).withFailMessage(body).isBetween(200, 299); + return typeManager.readValue(body, ContractNegotiationDto.class); + } + /** * Returns this participant's IDS ID */ @@ -182,15 +267,71 @@ public String getBpn() { return bpn; } - @Override - public void beforeAll(ExtensionContext context) throws Exception { - //only run this once - super.beforeTestExecution(context); + public String requestTransfer(String contractId, String assetId, Participant other, DataAddress destination, String dataRequestId) { + var response = baseRequest() + .when() + .body(TransferRequestDto.Builder.newInstance() + .assetId(assetId) + .id(dataRequestId) + .connectorAddress(other.idsEndpoint + "/data") + .managedResources(false) + .contractId(contractId) + .connectorId(bpn) + .protocol("ids-multipart") + .dataDestination(destination) + .build()) + .post("/transferprocess") + .then(); + + var body = response.extract().body().asString(); + assertThat(response.extract().statusCode()).withFailMessage(body).isBetween(200, 299); + + return typeManager.readValue(body, IdResponseDto.class).getId(); + } + + public TransferProcessDto getTransferProcess(String transferProcessId) { + var json = baseRequest() + .when() + .get("/transferprocess/" + transferProcessId) + .then() + .statusCode(allOf(greaterThanOrEqualTo(200), lessThan(300))) + .extract().body().asString(); + + return typeManager.readValue(json, TransferProcessDto.class); + + } + + public EndpointDataReference getDataReference(String dataRequestId) { + var dataReference = new AtomicReference(); + + var result = given() + .when() + .get(backend + "/{id}", dataRequestId) + .then() + .statusCode(200) + .extract() + .body() + .as(EndpointDataReference.class); + dataReference.set(result); + + return dataReference.get(); + } + + public String pullData(EndpointDataReference edr, Map queryParams) { + var response = given() + .baseUri(edr.getEndpoint()) + .header(edr.getAuthKey(), edr.getAuthCode()) + .queryParams(queryParams) + .when() + .get(); + assertThat(response.statusCode()).isBetween(200, 300); + return response.body().asString(); } @Override - public void afterAll(ExtensionContext context) throws Exception { - super.afterTestExecution(context); + protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { + super.bootExtensions(context, serviceExtensions); + wiper = new DataWiper(context); } private RequestSpecification baseRequest() { diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java index 145ae476f..f865d39d9 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java @@ -14,39 +14,30 @@ package org.eclipse.tractusx.edc.lifecycle; -import java.util.concurrent.TimeUnit; - import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; -public class TestRuntimeConfiguration { - - - public static final String IDS_PATH = "/api/v1/ids"; - - - public static final int PLATO_CONNECTOR_PORT = getFreePort(); - public static final int PLATO_MANAGEMENT_PORT = getFreePort(); - public static final String PLATO_CONNECTOR_PATH = "/api"; - public static final String PLATO_MANAGEMENT_PATH = "/api/v1/management"; - public static final String CONSUMER_CONNECTOR_MANAGEMENT_URL = "http://localhost:" + PLATO_MANAGEMENT_PORT + PLATO_MANAGEMENT_PATH; - public static final int PLATO_IDS_API_PORT = getFreePort(); - public static final String PLATO_IDS_API = "http://localhost:" + PLATO_IDS_API_PORT; - - public static final int SOKRATES_CONNECTOR_PORT = getFreePort(); - public static final int SOKRATES_MANAGEMENT_PORT = getFreePort(); - public static final String SOKRATES_CONNECTOR_PATH = "/api"; - public static final String SOKRATES_MANAGEMENT_PATH = "/api/v1/management"; - public static final int SOKRATES_IDS_API_PORT = getFreePort(); - public static final String SOKRATES_IDS_API = "http://localhost:" + SOKRATES_IDS_API_PORT; +class TestRuntimeConfiguration { - public static final String PROVIDER_IDS_API_DATA = "http://localhost:" + SOKRATES_IDS_API_PORT + IDS_PATH + "/data"; - public static final String PROVIDER_ASSET_ID = "test-document"; - public static final long CONTRACT_VALIDITY = TimeUnit.HOURS.toSeconds(1); + static final String IDS_PATH = "/api/v1/ids"; + static final int PLATO_CONNECTOR_PORT = getFreePort(); + static final int PLATO_MANAGEMENT_PORT = getFreePort(); + static final String PLATO_CONNECTOR_PATH = "/api"; + static final String PLATO_MANAGEMENT_PATH = "/api/v1/management"; + static final int PLATO_IDS_API_PORT = getFreePort(); + static final String PLATO_IDS_API = "http://localhost:" + PLATO_IDS_API_PORT; - public static final String SOKRATES_ASSET_FILE = "text-document.txt"; + static final int SOKRATES_CONNECTOR_PORT = getFreePort(); + static final int SOKRATES_MANAGEMENT_PORT = getFreePort(); + static final String SOKRATES_CONNECTOR_PATH = "/api"; + static final String SOKRATES_MANAGEMENT_PATH = "/api/v1/management"; + static final int SOKRATES_IDS_API_PORT = getFreePort(); + static final String SOKRATES_IDS_API = "http://localhost:" + SOKRATES_IDS_API_PORT; - public static final String PROVIDER_CONNECTOR_MANAGEMENT_URL = "http://localhost:" + SOKRATES_MANAGEMENT_PORT + SOKRATES_MANAGEMENT_PATH; + static final String SOKRATES_PUBLIC_API_PORT = String.valueOf(getFreePort()); + static final String PLATO_PUBLIC_API_PORT = String.valueOf(getFreePort()); + static final String PLATO_DATAPLANE_CONTROL_PORT = String.valueOf(getFreePort()); + static final String SOKRATES_DATAPLANE_CONTROL_PORT = String.valueOf(getFreePort()); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderEdcController.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderEdcController.java new file mode 100644 index 000000000..8ea80e745 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderEdcController.java @@ -0,0 +1,2 @@ +package org.eclipse.tractusx.edc.lifecycle.provider;public class ProviderEdcController { +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderServicesExtension.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderServicesExtension.java new file mode 100644 index 000000000..526ff6872 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderServicesExtension.java @@ -0,0 +1,2 @@ +package org.eclipse.tractusx.edc.lifecycle.provider;public class ProviderServicesExtension { +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/policy/PolicyHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/policy/PolicyHelperFunctions.java index b255aa078..56d92afbe 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/policy/PolicyHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/policy/PolicyHelperFunctions.java @@ -24,6 +24,7 @@ import org.eclipse.edc.policy.model.OrConstraint; import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.policy.model.PolicyType; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -53,4 +54,16 @@ public static PolicyDefinition businessPartnerNumberPolicy(String id, String... .build()) .build()).build()).build(); } + + public static PolicyDefinition noConstraintPolicy(String id) { + return PolicyDefinition.Builder.newInstance() + .id(id) + .policy(Policy.Builder.newInstance() + .permission(Permission.Builder.newInstance() + .action(Action.Builder.newInstance().type("USE").build()) + .build()) + .type(PolicyType.SET) + .build()) + .build(); + } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java index b7812ae4a..ec8045f5b 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java @@ -16,14 +16,8 @@ import org.eclipse.edc.api.query.QuerySpecDto; -import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.policy.model.Action; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.policy.model.PolicyType; import org.eclipse.tractusx.edc.lifecycle.MultiRuntimeTest; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,16 +26,11 @@ import static java.util.stream.IntStream.range; import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.tractusx.edc.policy.PolicyHelperFunctions.businessPartnerNumberPolicy; +import static org.eclipse.tractusx.edc.policy.PolicyHelperFunctions.noConstraintPolicy; @EndToEndTest public class CatalogTest extends MultiRuntimeTest { - - @BeforeAll - static void setup() { - - } - @Test void requestCatalog_fulfillsPolicy_shouldReturnOffer() { // arrange @@ -133,16 +122,6 @@ void requestCatalog_of1000Assets_shouldContainAll() { } - private PolicyDefinition noConstraintPolicy(String id) { - return PolicyDefinition.Builder.newInstance() - .id(id) - .policy(Policy.Builder.newInstance() - .permission(Permission.Builder.newInstance() - .action(Action.Builder.newInstance().type("USE").build()) - .build()) - .type(PolicyType.SET) - .build()) - .build(); - } + } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java new file mode 100644 index 000000000..78f3d2013 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.tests; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.eclipse.edc.connector.api.management.transferprocess.model.TransferProcessDto; +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates; +import org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.HttpDataAddress; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.edc.lifecycle.MultiRuntimeTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.time.Duration; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import static java.time.Duration.ofSeconds; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; +import static org.eclipse.edc.connector.transfer.dataplane.spi.TransferDataPlaneConstants.HTTP_PROXY; +import static org.eclipse.tractusx.edc.policy.PolicyHelperFunctions.noConstraintPolicy; + +@EndToEndTest +public class HttpConsumerPullWithProxyTest extends MultiRuntimeTest { + private static final Duration ASYNC_TIMEOUT = ofSeconds(45); + private static final Duration ASYNC_POLL_INTERVAL = ofSeconds(1); + private final long ONE_WEEK = 60 * 60 * 24 * 7; + MockWebServer server = new MockWebServer(); + + @Test + void transferData_privateBackend() throws IOException, InterruptedException { + var assetId = "api-asset-1"; + var url = server.url("/mock/api"); + server.start(); + + var authCodeHeaderName = "test-authkey"; + var authCode = "test-authcode"; + plato.createAsset(assetId, Map.of(), HttpDataAddress.Builder.newInstance() + .contentType("application/json") + .baseUrl(url.toString()) + .authKey(authCodeHeaderName) + .authCode(authCode) + .build()); + plato.createPolicy(noConstraintPolicy("policy-1")); + plato.createPolicy(noConstraintPolicy("policy-2")); + plato.createContractDefinition(assetId, "def-1", "policy-1", "policy-2", ONE_WEEK); + var negotiationId = sokrates.negotiateContract(plato, assetId); + + // forward declarations of our actual values + var transferProcessId = new AtomicReference(); + var dataRequestId = UUID.randomUUID().toString(); + var contractAgreementId = new AtomicReference(); + var edr = new AtomicReference(); + + + // wait for the successful contract negotiation + await().pollInterval(ASYNC_POLL_INTERVAL) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var negotiation = sokrates.getNegotiation(negotiationId); + assertThat(negotiation.getState()).isEqualTo(ContractNegotiationStates.CONFIRMED.toString()); + contractAgreementId.set(negotiation.getContractAgreementId()); + assertThat(contractAgreementId).isNotNull(); + transferProcessId.set(sokrates.requestTransfer(contractAgreementId.get(), assetId, plato, DataAddress.Builder.newInstance() + .type(HTTP_PROXY) + .build(), dataRequestId)); + assertThat(transferProcessId).isNotNull(); + }); + + // wait until transfer process completes + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var tp = sokrates.getTransferProcess(transferProcessId.get()); + assertThat(tp).isNotNull() + .extracting(TransferProcessDto::getState).isEqualTo(TransferProcessStates.COMPLETED.toString()); + }); + + // wait until EDC is available on the consumer side + server.enqueue(new MockResponse().setBody("test response").setResponseCode(200)); + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + edr.set(sokrates.getDataReference(dataRequestId)); + assertThat(edr).isNotNull(); + }); + + // pull data out of provider's backend service: + // Cons-DP -> Prov-DP -> Prov-backend + assertThat(sokrates.pullData(edr.get(), Map.of())).isEqualTo("test response"); + var rq = server.takeRequest(); + assertThat(rq.getHeader(authCodeHeaderName)).isEqualTo(authCode); + assertThat(rq.getHeader("Edc-Contract-Agreement-Id")).isEqualTo(contractAgreementId.get()); + assertThat(rq.getMethod()).isEqualToIgnoringCase("GET"); + } + + @AfterEach + void teardown() throws IOException { + server.shutdown(); + } +} diff --git a/edc-tests/runtime/build.gradle.kts b/edc-tests/runtime/build.gradle.kts index e4a832666..6f8a370af 100644 --- a/edc-tests/runtime/build.gradle.kts +++ b/edc-tests/runtime/build.gradle.kts @@ -21,12 +21,22 @@ plugins { dependencies { - runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) { + // use basic (all in-mem) control plane + implementation(project(":edc-controlplane:edc-controlplane-base")) { exclude("org.eclipse.edc", "oauth2-core") exclude("org.eclipse.edc", "oauth2-daps") exclude(module = "data-encryption") exclude(module = "control-plane-adapter") } + + // use basic (all in-mem) data plane + runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) { + exclude("org.eclipse.edc", "api-observability") + } + + implementation(edc.core.controlplane) + // for the controller + implementation(libs.jakarta.rsApi) } application { diff --git a/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java b/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java new file mode 100644 index 000000000..5ca08a922 --- /dev/null +++ b/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.lifecycle; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Path("/consumer") +public class ConsumerEdrHandlerController { + + private final Monitor monitor; + private Map dataReference; + + public ConsumerEdrHandlerController(Monitor monitor) { + this.monitor = monitor; + dataReference = new HashMap<>(); + } + + @Path("/datareference") + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + public void pushDataReference(EndpointDataReference edr) { + monitor.debug("Received new endpoint data reference with url " + edr.getEndpoint()); + dataReference.put(edr.getId(), edr); + } + + @Path("/datareference/{id}") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public EndpointDataReference getDataReference(@PathParam("id") String id) { + return Optional.ofNullable(dataReference.get(id)).orElseGet(() -> + { + monitor.warning("No EndpointDataReference found with id " + id); + return null; + }); + } + +} diff --git a/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java b/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java new file mode 100644 index 000000000..f46ef3e4e --- /dev/null +++ b/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.lifecycle; + +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.web.spi.WebService; + +public class ConsumerServicesExtension implements ServiceExtension { + @Inject + private WebService webService; + + @Override + public void initialize(ServiceExtensionContext context) { + webService.registerResource("default", new ConsumerEdrHandlerController(context.getMonitor())); + } +} diff --git a/edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..619665085 --- /dev/null +++ b/edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,15 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# 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: +# Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation +# +# + +org.eclipse.tractusx.edc.lifecycle.ConsumerServicesExtension diff --git a/settings.gradle.kts b/settings.gradle.kts index e0fc39433..22ac0d73a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -52,9 +52,9 @@ dependencyResolutionManagement { versionCatalogs { create("libs") { from("org.eclipse.edc:edc-versions:0.0.1-20230220-SNAPSHOT") - library("testcontainers-junit", "org.testcontainers","junit-jupiter").version("1.17.6") - library("apache-sshd-core", "org.apache.sshd","sshd-core").version("2.9.2") - library("apache-sshd-sftp", "org.apache.sshd","sshd-sftp").version("2.9.2") + library("testcontainers-junit", "org.testcontainers", "junit-jupiter").version("1.17.6") + library("apache-sshd-core", "org.apache.sshd", "sshd-core").version("2.9.2") + library("apache-sshd-sftp", "org.apache.sshd", "sshd-sftp").version("2.9.2") } // create version catalog for all EDC modules create("edc") { @@ -86,6 +86,9 @@ dependencyResolutionManagement { library("api-management", "org.eclipse.edc", "management-api").versionRef("edc") library("api-catalog", "org.eclipse.edc", "catalog-api").versionRef("edc") library("api-observability", "org.eclipse.edc", "api-observability").versionRef("edc") + library("api-contractnegotiation", "org.eclipse.edc", "contract-negotiation-api").versionRef("edc") + library("api-dataplane", "org.eclipse.edc", "data-plane-api").versionRef("edc") + library("api-transferprocess", "org.eclipse.edc", "transfer-process-api").versionRef("edc") library("ext-http", "org.eclipse.edc", "http").versionRef("edc") library("spi-ids", "org.eclipse.edc", "ids-spi").versionRef("edc") library("ids", "org.eclipse.edc", "ids").versionRef("edc") @@ -140,6 +143,8 @@ dependencyResolutionManagement { "transfer-pull-http-dynamic-receiver" ).versionRef("edc") + library("transfer.receiver", "org.eclipse.edc", "transfer-pull-http-receiver").versionRef("edc") + bundle( "connector", listOf("boot", "core-connector", "core-jersey", "core-controlplane", "api-observability") From d957581c552a4788db5f708559072da137f74d44 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Mon, 3 Apr 2023 10:32:58 +0200 Subject: [PATCH 033/132] docs: create decision record about renaming git branches --- .../2023-04-03_renaming_branches/README.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/development/decision-records/2023-04-03_renaming_branches/README.md diff --git a/docs/development/decision-records/2023-04-03_renaming_branches/README.md b/docs/development/decision-records/2023-04-03_renaming_branches/README.md new file mode 100644 index 000000000..dcb80865c --- /dev/null +++ b/docs/development/decision-records/2023-04-03_renaming_branches/README.md @@ -0,0 +1,61 @@ +# Renaming Git branches to comply with TractusX standards + +## Decision + +TractusX-EDC will rename its Git branching structure to comply with TractusX release guidelines, and to be able to +leverage +GitHub convenience features, while continuing to use the Gitflow branching model. + +## Rationale + +The TractusX organization has established +a [release guideline](https://eclipse-tractusx.github.io/docs/release/trg-2/trg-2-1/) which mandates that all projects' +default branch be called `main`. + +### Selecting default branches + +In GitHub, the default branch has a couple of important features attached to it: + +- cloning or forking the repository will automatically check out the default branch +- when creating pull requests the default branch is targeted by default +- [automatic issue linking and closing](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) + only works with the default branch + +### The problem with GitFlow + +The GitFlow branching model suggests that the day-to-day work be done on a branch called `develop`, while the `main` +branch stores the version history and only receives (merge) commits after a version releases. + +This would call for `develop` being the GitHub default branch, which is forbidden by the aforementioned release +guideline. + +## Approach + +In order to comply with the TractusX release guideline, to make use of the GitHub features _and_ also use GitFlow, we +propose renaming a couple of branches. While GitFlow _suggests_ branch names, it does not _require_ it, and most +tools allow for customizing them anyway. Thus, from an abstract perspective, the following changes are necessary: + +- `main` becomes our work/development branch. All pull requests target `main`. +- `develop` gets deleted +- a new branch `releases` is introduced, which tracks the release history and receives post-release merge commits. + +Technically this will involve force-pushing, which is a potentially destructive operation. Therefor the following +section outlines the exact sequence of steps. Note that "upstream" refers to `eclipse-tractusx/tractusx-edc`, while " +fork" refers to `catenax-ng/tx-tractusx-edc`. + +- create a new branch `upstream/releases` +- create a new branch `fork/releaes`, set it to track `upstream/releases` +- push the contents of `fork/main` -> `upstream/releases` +- synchronize `upstream/develop` with `fork/develop` +- force-push the contents of `develop` -> `upstream/main` (do **not** update the tracking branch!) +- synchronize `upstream/main` -> `fork/main`. +- delete/archive `upstream/develop` and `fork/develop` + +_Note that most of this will likely need to be done manually, since GitHub does not allow for advanced Git operations +like force-pushing. Write access to `upstream` is required!_ + +## Further notes + +The new `releases` branch (note the plural) will serve the same purpose that `main` did up until now, which is to track +all releases (via merge commits and tags) in chronological order. We will continue to have separate `release/x.y.z` +branches for every release. \ No newline at end of file From 8ddae2dac2007b955de8fa1dc6eb1ac7eac8e539 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Mon, 3 Apr 2023 11:26:20 +0200 Subject: [PATCH 034/132] removed obsolete HTTP test --- .../edc/tests/HttpProxyTransferSteps.java | 16 ++++++---------- .../features/HttpProxyDataTransfer.feature | 18 ------------------ 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java index 6f34f5eb0..24f68e3a1 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java @@ -6,11 +6,9 @@ import io.cucumber.java.en.When; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.edc.tests.data.Asset; -import org.eclipse.tractusx.edc.tests.data.ContractNegotiation; import org.eclipse.tractusx.edc.tests.data.DataAddress; import org.eclipse.tractusx.edc.tests.data.HttpProxySinkDataAddress; import org.eclipse.tractusx.edc.tests.data.HttpProxySourceDataAddress; -import org.eclipse.tractusx.edc.tests.data.Transfer; import org.junit.jupiter.api.Assertions; import java.io.IOException; @@ -61,19 +59,17 @@ public void hasAssets(Connector connector, DataTable table) throws Exception { @When("'{connector}' initiates HttpProxy transfer from '{connector}'") public void sokratesInitiateHttpProxyTransferProcessFromPlato( Connector consumer, Connector provider, DataTable dataTable) throws IOException { - final DataManagementAPI api = consumer.getDataManagementAPI(); - final String receiverUrl = provider.getEnvironment().getIdsUrl() + "/data"; + var api = consumer.getDataManagementAPI(); + var receiverUrl = provider.getEnvironment().getIdsUrl() + "/data"; - final List negotiation = api.getNegotiations(); - final String agreementId = negotiation.get(0).getAgreementId(); - final DataAddress dataAddress = new HttpProxySinkDataAddress(); + var negotiation = api.getNegotiations(); + var agreementId = negotiation.get(0).getAgreementId(); + var dataAddress = new HttpProxySinkDataAddress(); for (var map : dataTable.asMaps()) { final String assetId = map.get(ASSET_ID); final String receiverHttpEndpoint = map.get(RECEIVER_HTTP_ENDPOINT); - final Transfer transfer = - api.initiateTransferProcess( - receiverUrl, agreementId, assetId, dataAddress, receiverHttpEndpoint); + var transfer = api.initiateTransferProcess(receiverUrl, agreementId, assetId, dataAddress, receiverHttpEndpoint); transfer.waitUntilComplete(api); } diff --git a/edc-tests/cucumber/src/test/resources/org/eclipse/tractusx/edc/tests/features/HttpProxyDataTransfer.feature b/edc-tests/cucumber/src/test/resources/org/eclipse/tractusx/edc/tests/features/HttpProxyDataTransfer.feature index d318ad745..b04970ec7 100644 --- a/edc-tests/cucumber/src/test/resources/org/eclipse/tractusx/edc/tests/features/HttpProxyDataTransfer.feature +++ b/edc-tests/cucumber/src/test/resources/org/eclipse/tractusx/edc/tests/features/HttpProxyDataTransfer.feature @@ -24,24 +24,6 @@ Feature: HttpProxy Data Transfer Given 'Plato' has an empty database Given 'Sokrates' has an empty database - Scenario: Connector transfers data via HttpProxy - Given 'Plato' has a http proxy assets - | id | description | baseUrl | - | asset-1 | http proxy transfer asset | http://localhost:8081/api/check/liveness | - And 'Plato' has the following policies - | id | action | - | policy-1 | USE | - And 'Plato' has the following contract definitions - | id | access policy | contract policy | asset | - | contract-definition-1 | policy-1 | policy-1 | asset-1 | - When 'Sokrates' negotiates the contract successfully with 'Plato' - | contract offer id | asset id | policy id | - | contract-definition-1 | asset-1 | policy-1 | - And 'Sokrates' initiates HttpProxy transfer from 'Plato' - | asset id | receiverHttpEndpoint | - | asset-1 | http://backend:8080 | - Then the backend application of 'Sokrates' has received data - Scenario: Connector transfers data via HttpProxy, data on provider side requires oauth2 authentication Given 'Plato' has a http proxy assets | id | description | baseUrl | oauth2 token url | oauth2 client id | oauth2 client secret | oauth2 scope | From 97c6e4bd1d4265d89e32e18d5ba4c9321a6696b9 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Mon, 3 Apr 2023 18:02:39 +0200 Subject: [PATCH 035/132] feat(charts): removes edc-controlplane and edc-dataplane charts --- charts/edc-controlplane/.helmignore | 29 -- charts/edc-controlplane/Chart.yaml | 35 -- charts/edc-controlplane/LICENSE | 202 ---------- charts/edc-controlplane/README.md | 106 ----- charts/edc-controlplane/README.md.gotmpl | 26 -- charts/edc-controlplane/templates/NOTES.txt | 74 ---- .../edc-controlplane/templates/_helpers.tpl | 72 ---- .../templates/configmap-env.yaml | 32 -- .../edc-controlplane/templates/configmap.yaml | 49 --- .../templates/deployment.yaml | 154 ------- charts/edc-controlplane/templates/hpa.yaml | 52 --- .../templates/imagepullsecret.yaml | 35 -- .../edc-controlplane/templates/ingress.yaml | 100 ----- .../edc-controlplane/templates/service.yaml | 59 --- .../templates/serviceaccount.yaml | 36 -- charts/edc-controlplane/values.yaml | 379 ------------------ charts/edc-dataplane/.helmignore | 29 -- charts/edc-dataplane/Chart.yaml | 35 -- charts/edc-dataplane/LICENSE | 202 ---------- charts/edc-dataplane/README.md | 90 ----- charts/edc-dataplane/README.md.gotmpl | 26 -- charts/edc-dataplane/templates/NOTES.txt | 64 --- charts/edc-dataplane/templates/_helpers.tpl | 72 ---- .../templates/configmap-env.yaml | 32 -- charts/edc-dataplane/templates/configmap.yaml | 45 --- .../edc-dataplane/templates/deployment.yaml | 142 ------- charts/edc-dataplane/templates/hpa.yaml | 52 --- .../templates/imagepullsecret.yaml | 35 -- charts/edc-dataplane/templates/ingress.yaml | 100 ----- charts/edc-dataplane/templates/service.yaml | 51 --- .../templates/serviceaccount.yaml | 36 -- charts/edc-dataplane/values.yaml | 331 --------------- 32 files changed, 2782 deletions(-) delete mode 100644 charts/edc-controlplane/.helmignore delete mode 100644 charts/edc-controlplane/Chart.yaml delete mode 100644 charts/edc-controlplane/LICENSE delete mode 100644 charts/edc-controlplane/README.md delete mode 100644 charts/edc-controlplane/README.md.gotmpl delete mode 100644 charts/edc-controlplane/templates/NOTES.txt delete mode 100644 charts/edc-controlplane/templates/_helpers.tpl delete mode 100644 charts/edc-controlplane/templates/configmap-env.yaml delete mode 100644 charts/edc-controlplane/templates/configmap.yaml delete mode 100644 charts/edc-controlplane/templates/deployment.yaml delete mode 100644 charts/edc-controlplane/templates/hpa.yaml delete mode 100644 charts/edc-controlplane/templates/imagepullsecret.yaml delete mode 100644 charts/edc-controlplane/templates/ingress.yaml delete mode 100644 charts/edc-controlplane/templates/service.yaml delete mode 100644 charts/edc-controlplane/templates/serviceaccount.yaml delete mode 100644 charts/edc-controlplane/values.yaml delete mode 100644 charts/edc-dataplane/.helmignore delete mode 100644 charts/edc-dataplane/Chart.yaml delete mode 100644 charts/edc-dataplane/LICENSE delete mode 100644 charts/edc-dataplane/README.md delete mode 100644 charts/edc-dataplane/README.md.gotmpl delete mode 100644 charts/edc-dataplane/templates/NOTES.txt delete mode 100644 charts/edc-dataplane/templates/_helpers.tpl delete mode 100644 charts/edc-dataplane/templates/configmap-env.yaml delete mode 100644 charts/edc-dataplane/templates/configmap.yaml delete mode 100644 charts/edc-dataplane/templates/deployment.yaml delete mode 100644 charts/edc-dataplane/templates/hpa.yaml delete mode 100644 charts/edc-dataplane/templates/imagepullsecret.yaml delete mode 100644 charts/edc-dataplane/templates/ingress.yaml delete mode 100644 charts/edc-dataplane/templates/service.yaml delete mode 100644 charts/edc-dataplane/templates/serviceaccount.yaml delete mode 100644 charts/edc-dataplane/values.yaml diff --git a/charts/edc-controlplane/.helmignore b/charts/edc-controlplane/.helmignore deleted file mode 100644 index 148b31d6c..000000000 --- a/charts/edc-controlplane/.helmignore +++ /dev/null @@ -1,29 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ - -README.md.gotmpl - -# Accept only values.yaml -values?*.yaml -values?*.yml diff --git a/charts/edc-controlplane/Chart.yaml b/charts/edc-controlplane/Chart.yaml deleted file mode 100644 index ffd77bd4d..000000000 --- a/charts/edc-controlplane/Chart.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v2 -name: edc-controlplane -description: >- - EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers -home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-controlplane -type: application -appVersion: "0.3.2" -version: 0.3.2 -deprecated: true -maintainers: [] -sources: - - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-controlplane diff --git a/charts/edc-controlplane/LICENSE b/charts/edc-controlplane/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/charts/edc-controlplane/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/charts/edc-controlplane/README.md b/charts/edc-controlplane/README.md deleted file mode 100644 index 9984db480..000000000 --- a/charts/edc-controlplane/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# edc-controlplane - -> **:exclamation: This Helm Chart is deprecated!** - -![Version: 0.3.2](https://img.shields.io/badge/Version-0.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.2](https://img.shields.io/badge/AppVersion-0.3.2-informational?style=flat-square) - -EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers - -**Homepage:** - -## TL;DR - -```shell -helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-controlplane --version 0.3.2 -``` - -## Source Code - -* - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | -| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | -| autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | -| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.dataplane.token.validation.endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.proxy.token.validity.seconds=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins=\n# ids.webhook.address="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-edc/Connector) | -| customLabels | object | `{}` | Additional custom Labels to add | -| edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | -| edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | -| edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | -| edc.endpoints.data.port | string | `"8181"` | The network port, which the "data" management api is going to be exposed by the container, pod and service | -| edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed at | -| edc.endpoints.default.port | string | `"8080"` | The network port, which the "default" api is going to be exposed by the container, pod and service | -| edc.endpoints.ids.path | string | `"/api/v1/ids"` | The path mapping the "ids" multipart api is going to be exposed at | -| edc.endpoints.ids.port | string | `"8282"` | The network port, which the "ids" multipart api is going to be exposed by the container, pod and service | -| edc.endpoints.metrics.path | string | `"/metrics"` | The path mapping the prometheus metrics are going to be exposed at | -| edc.endpoints.metrics.port | string | `"9090"` | The network port, which the prometheus metrics are going to be exposed by the container, pod and service | -| edc.endpoints.validation.path | string | `"/validation"` | The path mapping the "validation" api is going to be exposed at | -| edc.endpoints.validation.port | string | `"8182"` | The network port, which the "validation" api is going to be exposed by the container, pod and service | -| env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | -| envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | -| fullnameOverride | string | `""` | Overrides the releases full name | -| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-memory] | -| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | -| imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | -| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | -| ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | -| ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| ingresses[0].enabled | bool | `true` | | -| ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | -| ingresses[0].hostname | string | `"edc-controlplane.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | -| ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | -| ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| ingresses[1].enabled | bool | `false` | | -| ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | -| ingresses[1].hostname | string | `"edc-controlplane.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | -| livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| logging.properties | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) | -| nameOverride | string | `""` | Overrides the charts name | -| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | -| opentelemetry.properties | string | `"otel.javaagent.enabled=true\notel.javaagent.debug=false"` | opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) | -| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | -| podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | -| podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | -| podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | -| podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | -| readinessProbe.enabled | bool | `true` | Whether to enable kubernetes readiness-probes | -| replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | -| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | -| securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | -| securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | -| securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | -| securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | -| securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | -| service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | -| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | -| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | -| startupProbe.enabled | bool | `true` | Whether to enable kubernetes startup-probes | -| startupProbe.failureThreshold | int | `12` | Minimum consecutive failures for the probe to be considered failed after having succeeded | -| startupProbe.initialDelaySeconds | int | `10` | Number of seconds after the container has started before liveness probes are initiated. | -| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | -| volumeMounts | list | `[]` | Additional volumeMounts to the controlplane main container | -| volumes | list | `[]` | Additional volumes to the controlplane pod | - ----------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/edc-controlplane/README.md.gotmpl b/charts/edc-controlplane/README.md.gotmpl deleted file mode 100644 index aa70ec6fc..000000000 --- a/charts/edc-controlplane/README.md.gotmpl +++ /dev/null @@ -1,26 +0,0 @@ -{{ template "chart.header" . }} - -{{ template "chart.deprecationWarning" . }} - -{{ template "chart.badgesSection" . }} - -{{ template "chart.description" . }} - -{{ template "chart.homepageLine" . }} - -## TL;DR - -```shell -helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-controlplane --version {{ .Version }} -``` - -{{ template "chart.maintainersSection" . }} - -{{ template "chart.sourcesSection" . }} - -{{ template "chart.requirementsSection" . }} - -{{ template "chart.valuesSection" . }} - -{{ template "helm-docs.versionFooter" . }} diff --git a/charts/edc-controlplane/templates/NOTES.txt b/charts/edc-controlplane/templates/NOTES.txt deleted file mode 100644 index 6758c6bdf..000000000 --- a/charts/edc-controlplane/templates/NOTES.txt +++ /dev/null @@ -1,74 +0,0 @@ - -CHART NAME: {{ .Chart.Name }} -CHART VERSION: {{ .Chart.Version }} -APP VERSION: {{ .Chart.AppVersion }} - -Logs can be accessed by running this command: - - kubectl logs --tail 100 -f \ - --namespace {{ .Release.Namespace }} \ - -l "app.kubernetes.io/name={{ include "edc-controlplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" - -{{- if .Values.ingresses }} - -Following ingress URLS are available: - {{- $edcEndpoints := .Values.edc.endpoints }} - {{- range .Values.ingresses }} - {{- if .enabled }} - {{- $ingressEdcEndpoints := .endpoints }} - {{- $hostname := .hostname }} - {{- $tls := .tls }} - {{- range $name, $mapping := $edcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - Visit http{{ if $tls }}s{{ end }}://{{ $hostname }}{{ $mapping.path }} to access the {{ $name }} api - {{- end }} - {{- end }} - {{- end }} - {{- end }} - -{{- else if contains "NodePort" .Values.service.type }} -Get the application URLs by running these commands: - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - - export NODE_PORT_DEFAULT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_DATA=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_VALIDATION=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[2].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_CONTROL=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[3].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_IDS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[4].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_METRICS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[5].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - - echo "Visit http://$NODE_IP:$NODE_PORT_DEFAULT to access the default api" - echo "Visit http://$NODE_IP:$NODE_PORT_DATA to access the data management api" - echo "Visit http://$NODE_IP:$NODE_PORT_VALIDATION to access the data transfer validation api" - echo "Visit http://$NODE_IP:$NODE_PORT_CONTROL to access the control api" - echo "Visit http://$NODE_IP:$NODE_PORT_IDS to access the IDS api" - echo "Visit http://$NODE_IP:$NODE_PORT_METRICS to access the metrics api" - -{{- else if contains "ClusterIP" .Values.service.type }} -Get the application URL by running these commands: - - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "edc-controlplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - - export CONTAINER_PORT_DEFAULT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - export CONTAINER_PORT_DATA=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[1].containerPort}") - export CONTAINER_PORT_VALIDATION=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[2].containerPort}") - export CONTAINER_PORT_CONTROL=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[3].containerPort}") - export CONTAINER_PORT_IDS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[4].containerPort}") - export CONTAINER_PORT_METRICS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[5].containerPort}") - - echo "Visit http://127.0.0.1:8080 to access the default api" - echo "Visit http://127.0.0.1:8182 to access the data management api" - echo "Visit http://127.0.0.1:8182 to access the data transfer validation api" - echo "Visit http://127.0.0.1:9999 to access the control api" - echo "Visit http://127.0.0.1:8282 to access the IDS api" - echo "Visit http://127.0.0.1:9090 to access the metrics api" - - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME \ - 8080:$CONTAINER_PORT_DEFAULT \ - 8182:$CONTAINER_PORT_DATA \ - 8182:$CONTAINER_PORT_VALIDATION \ - 9999:$CONTAINER_PORT_CONTROL \ - 8282:$CONTAINER_PORT_IDS \ - 9090:$CONTAINER_PORT_METRICS - -{{- end }} diff --git a/charts/edc-controlplane/templates/_helpers.tpl b/charts/edc-controlplane/templates/_helpers.tpl deleted file mode 100644 index 272a0f27d..000000000 --- a/charts/edc-controlplane/templates/_helpers.tpl +++ /dev/null @@ -1,72 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "edc-controlplane.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "edc-controlplane.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "edc-controlplane.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "edc-controlplane.labels" -}} -helm.sh/chart: {{ include "edc-controlplane.chart" . }} -{{ include "edc-controlplane.selectorLabels" . }} -{{ include "edc-controlplane.customLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "edc-controlplane.selectorLabels" -}} -app.kubernetes.io/name: {{ include "edc-controlplane.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Custom labels -*/}} -{{- define "edc-controlplane.customLabels" -}} -{{- with .Values.customLabels }} -{{ toYaml . }} -{{- end }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "edc-controlplane.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "edc-controlplane.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/charts/edc-controlplane/templates/configmap-env.yaml b/charts/edc-controlplane/templates/configmap-env.yaml deleted file mode 100644 index d33071a58..000000000 --- a/charts/edc-controlplane/templates/configmap-env.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "edc-controlplane.fullname" . }}-env - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -data: - {{- toYaml .Values.env | nindent 2 }} diff --git a/charts/edc-controlplane/templates/configmap.yaml b/charts/edc-controlplane/templates/configmap.yaml deleted file mode 100644 index 863ac5e83..000000000 --- a/charts/edc-controlplane/templates/configmap.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "edc-controlplane.fullname" . }}-configmap - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -data: - configuration.properties: |- - web.http.default.port={{ .Values.edc.endpoints.default.port }} - web.http.default.path={{ .Values.edc.endpoints.default.path }} - web.http.data.port={{ .Values.edc.endpoints.data.port }} - web.http.data.path={{ .Values.edc.endpoints.data.path }} - web.http.validation.port={{ .Values.edc.endpoints.validation.port }} - web.http.validation.path={{ .Values.edc.endpoints.validation.path }} - web.http.control.port={{ .Values.edc.endpoints.control.port }} - web.http.control.path={{ .Values.edc.endpoints.control.path }} - web.http.ids.port={{ .Values.edc.endpoints.ids.port }} - web.http.ids.path={{ .Values.edc.endpoints.ids.path }} - {{- .Values.configuration.properties | nindent 4 }} - - opentelemetry.properties: |- - {{- .Values.opentelemetry.properties | nindent 4 }} - - logging.properties: |- - {{- .Values.logging.properties | nindent 4 }} diff --git a/charts/edc-controlplane/templates/deployment.yaml b/charts/edc-controlplane/templates/deployment.yaml deleted file mode 100644 index 4fd762d0b..000000000 --- a/charts/edc-controlplane/templates/deployment.yaml +++ /dev/null @@ -1,154 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "edc-controlplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "edc-controlplane.selectorLabels" . | nindent 6 }} - template: - metadata: - annotations: - checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - checksum/env-config: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} - {{- with .Values.podAnnotations }} - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "edc-controlplane.selectorLabels" . | nindent 8 }} - spec: - {{- if .Values.imagePullSecret.dockerconfigjson }} - imagePullSecrets: - - name: {{ include "edc-controlplane.fullname" . }}-imagepullsecret - {{- else }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- end }} - serviceAccountName: {{ include "edc-controlplane.serviceAccountName" . }} - automountServiceAccountToken: {{ if .Values.automountServiceAccountToken }}true{{ else }}false{{ end }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: default - containerPort: {{ .Values.edc.endpoints.default.port }} - protocol: TCP - - name: data - containerPort: {{ .Values.edc.endpoints.data.port }} - protocol: TCP - - name: validation - containerPort: {{ .Values.edc.endpoints.validation.port }} - protocol: TCP - - name: control - containerPort: {{ .Values.edc.endpoints.control.port }} - protocol: TCP - - name: ids - containerPort: {{ .Values.edc.endpoints.ids.port }} - protocol: TCP - - name: metrics - containerPort: {{ .Values.edc.endpoints.metrics.port }} - protocol: TCP - {{- if .Values.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/liveness - port: default - {{- end }} - {{- if .Values.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/readiness - port: default - {{- end }} - {{- if .Values.startupProbe.enabled }} - startupProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/startup - port: default - failureThreshold: {{ .Values.startupProbe.failureThreshold }} - initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} - {{- end }} - envFrom: - - configMapRef: - name: {{ include "edc-controlplane.fullname" . }}-env - {{- if .Values.envSecretName }} - - secretRef: - name: {{ .Values.envSecretName | quote }} - {{- end }} - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: configuration - mountPath: /app/configuration.properties - subPath: configuration.properties - - name: configuration - mountPath: /app/opentelemetry.properties - subPath: opentelemetry.properties - - name: configuration - mountPath: /app/logging.properties - subPath: logging.properties - {{- with .Values.volumeMounts }} - {{- toYaml . | nindent 12 }} - {{- end }} - volumes: - - name: configuration - configMap: - name: {{ include "edc-controlplane.fullname" . }}-configmap - items: - - key: configuration.properties - path: configuration.properties - - key: opentelemetry.properties - path: opentelemetry.properties - - key: logging.properties - path: logging.properties - {{- with .Values.volumes }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/charts/edc-controlplane/templates/hpa.yaml b/charts/edc-controlplane/templates/hpa.yaml deleted file mode 100644 index bc75d097a..000000000 --- a/charts/edc-controlplane/templates/hpa.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.autoscaling.enabled }} ---- -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "edc-controlplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "edc-controlplane.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/edc-controlplane/templates/imagepullsecret.yaml b/charts/edc-controlplane/templates/imagepullsecret.yaml deleted file mode 100644 index 6b6e29ace..000000000 --- a/charts/edc-controlplane/templates/imagepullsecret.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.imagePullSecret.dockerconfigjson }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "edc-controlplane.fullname" . }}-imagepullsecret - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -data: - .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} -type: kubernetes.io/dockerconfigjson -{{- end }} diff --git a/charts/edc-controlplane/templates/ingress.yaml b/charts/edc-controlplane/templates/ingress.yaml deleted file mode 100644 index cb58b5ac9..000000000 --- a/charts/edc-controlplane/templates/ingress.yaml +++ /dev/null @@ -1,100 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG - # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH - # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation - # - # See the NOTICE file(s) distributed with this work for additional - # information regarding copyright ownership. - # - # 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 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - # License for the specific language governing permissions and limitations - # under the License. - # - # SPDX-License-Identifier: Apache-2.0 - # - -{{- $fullName := include "edc-controlplane.fullname" . }} -{{- $labels := include "edc-controlplane.labels" . | nindent 4 }} -{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} -{{- $edcEndpoints := .Values.edc.endpoints }} -{{- $namespace := .Release.Namespace }} -{{- range .Values.ingresses }} -{{- if and .enabled .endpoints }} -{{- $ingressName := printf "%s-%s" $fullName .hostname }} ---- -{{- if semverCompare ">=1.19-0" $gitVersion }} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" $gitVersion }} -apiVersion: networking.k8s.io/v1beta1 -{{- else }} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $ingressName }} - namespace: {{ $namespace | default "default" | quote }} - labels: - {{- $labels | nindent 2 }} - annotations: - {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} - {{- if not (hasKey .annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .annotations "kubernetes.io/ingress.class" .className}} - {{- end }} - {{- end }} - {{- if .certManager }} - {{- if .certManager.issuer }} - {{- $_ := set .annotations "cert-manager.io/issuer" .certManager.issuer}} - {{- end }} - {{- if .certManager.clusterIssuer }} - {{- $_ := set .annotations "cert-manager.io/cluster-issuer" .certManager.clusterIssuer}} - {{- end }} - {{- end }} - {{- with .annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .className (semverCompare ">=1.18-0" $gitVersion) }} - ingressClassName: {{ .className }} - {{- end }} - {{- if .hostname }} - {{- if .tls.enabled }} - tls: - - hosts: - - {{ .hostname }} - {{- if .tls.secretName }} - secretName: {{ .tls.secretName }} - {{- else }} - secretName: {{ $ingressName }}-tls - {{- end }} - {{- end }} - rules: - - host: {{ .hostname }} - http: - paths: - {{- $ingressEdcEndpoints := .endpoints }} - {{- range $name, $mapping := $edcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - - path: {{ $mapping.path }} - pathType: Prefix - backend: - {{- if semverCompare ">=1.19-0" $gitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $mapping.port }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $mapping.port }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} -{{- end }}{{- /* end: if .enabled */}} -{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/charts/edc-controlplane/templates/service.yaml b/charts/edc-controlplane/templates/service.yaml deleted file mode 100644 index 18bc8bd55..000000000 --- a/charts/edc-controlplane/templates/service.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "edc-controlplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.edc.endpoints.default.port }} - targetPort: default - protocol: TCP - name: default - - port: {{ .Values.edc.endpoints.control.port }} - targetPort: control - protocol: TCP - name: control - - port: {{ .Values.edc.endpoints.data.port }} - targetPort: data - protocol: TCP - name: data - - port: {{ .Values.edc.endpoints.validation.port }} - targetPort: validation - protocol: TCP - name: validation - - port: {{ .Values.edc.endpoints.ids.port }} - targetPort: ids - protocol: TCP - name: ids - - port: {{ .Values.edc.endpoints.metrics.port }} - targetPort: metrics - protocol: TCP - name: metrics - selector: - {{- include "edc-controlplane.selectorLabels" . | nindent 4 }} diff --git a/charts/edc-controlplane/templates/serviceaccount.yaml b/charts/edc-controlplane/templates/serviceaccount.yaml deleted file mode 100644 index 1f9d5045b..000000000 --- a/charts/edc-controlplane/templates/serviceaccount.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.serviceAccount.create -}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "edc-controlplane.serviceAccountName" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/charts/edc-controlplane/values.yaml b/charts/edc-controlplane/values.yaml deleted file mode 100644 index b43d67a35..000000000 --- a/charts/edc-controlplane/values.yaml +++ /dev/null @@ -1,379 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -# Default values for edc-controlplane. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -# -- Specifies how many replicas of a deployed pod shall be created during the deployment -# Note: If horizontal pod autoscaling is enabled this setting has no effect -replicaCount: 1 - -image: - # -- Which derivate of the edc control-plane to use. - # One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-memory] - repository: ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecret: - # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) - # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). - # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. - dockerconfigjson: "" - -# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) -imagePullSecrets: [] - -# -- Overrides the charts name -nameOverride: "" - -# -- Overrides the releases full name -fullnameOverride: "" - -# -- Additional custom Labels to add -customLabels: {} - -serviceAccount: - # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release - create: true - # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account - annotations: {} - # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template - name: "" - -# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod -automountServiceAccountToken: false - -# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) -podAnnotations: {} - -# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment -podSecurityContext: - seccompProfile: - # -- Restrict a Container's Syscalls with seccomp - type: RuntimeDefault - # -- Runs all processes within a pod with a special uid - runAsUser: 10001 - # -- Processes within a pod will belong to this guid - runAsGroup: 10001 - # -- The owner for volumes and any files created within volumes will belong to this guid - fsGroup: 10001 - -# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod -securityContext: - capabilities: - # -- Specifies which capabilities to drop to reduce syscall attack surface - drop: - - ALL - # -- Specifies which capabilities to add to issue specialized syscalls - add: [] - # -- Whether the root filesystem is mounted in read-only mode - readOnlyRootFilesystem: true - # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID - allowPrivilegeEscalation: false - # -- Requires the container to run without root privileges - runAsNonRoot: true - # -- The container's process will run with the specified uid - runAsUser: 10001 - -livenessProbe: - # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - -readinessProbe: - # -- Whether to enable kubernetes readiness-probes - enabled: true - -startupProbe: - # -- Whether to enable kubernetes startup-probes - enabled: true - # -- Minimum consecutive failures for the probe to be considered failed after having succeeded - failureThreshold: 12 - # -- Number of seconds after the container has started before liveness probes are initiated. - initialDelaySeconds: 10 - -# -- Additional volumeMounts to the controlplane main container -volumeMounts: [] - -# -- Additional volumes to the controlplane pod -volumes: [] - -## EDC endpoints exposed by the control-plane -edc: - endpoints: - ## Default api exposing health checks etc - default: - # -- The network port, which the "default" api is going to be exposed by the container, pod and service - port: "8080" - # -- The path mapping the "default" api is going to be exposed at - path: /api - ## Data management API - data: - # -- The network port, which the "data" management api is going to be exposed by the container, pod and service - port: "8181" - # -- The path mapping the "data" management api is going to be exposed at - path: /data - ## Validation API - validation: - # -- The network port, which the "validation" api is going to be exposed by the container, pod and service - port: "8182" - # -- The path mapping the "validation" api is going to be exposed at - path: /validation - ## Control API - control: - # -- The network port, which the "control" api is going to be exposed by the container, pod and service - port: "9999" - # -- The path mapping the "control" api is going to be exposed at - path: /api/controlplane/control - ## IDS endpoints - ids: - # -- The network port, which the "ids" multipart api is going to be exposed by the container, pod and service - port: "8282" - # -- The path mapping the "ids" multipart api is going to be exposed at - path: /api/v1/ids - ## Prometheus endpoint - metrics: - # -- The network port, which the prometheus metrics are going to be exposed by the container, pod and service - port: "9090" - # -- The path mapping the prometheus metrics are going to be exposed at - path: /metrics - -service: - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. - type: ClusterIP - -## Ingress declaration to expose the network service. -ingresses: - ## Public / Internet facing Ingress - - enabled: true - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-controlplane.local" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - ids - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - - ## Private / Intranet facing Ingress - - enabled: false - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-controlplane.intranet" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - data - - control - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - -# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod -resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - -# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. -nodeSelector: {} - -# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. -tolerations: [] - -# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. -affinity: {} - -# -- Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) -# Ex.: -# JAVA_TOOL_OPTIONS: > -# -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 -env: {} - -# -- [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from -envSecretName: - -logging: - # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) - properties: |- - .level=INFO - org.eclipse.edc.level=ALL - handlers=java.util.logging.ConsoleHandler - java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter - java.util.logging.ConsoleHandler.level=ALL - java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n - -opentelemetry: - # -- opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) - properties: |- - otel.javaagent.enabled=true - otel.javaagent.debug=false - -configuration: - # -- EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-edc/Connector) - properties: |- - # edc.api.auth.key= - # edc.atomikos.checkpoint.interval= - # edc.atomikos.directory= - # edc.atomikos.logging= - # edc.atomikos.threaded2pc= - # edc.atomikos.timeout= - # edc.aws.access.key= - # edc.aws.provision.retry.retries.max= - # edc.aws.provision.role.duration.session.max= - # edc.aws.secret.access.key= - # edc.blobstore.endpoint= - # edc.dataplane.token.validation.endpoint= - # edc.core.retry.backoff.max= - # edc.core.retry.backoff.min= - # edc.core.retry.retries.max= - # edc.core.system.health.check.liveness-period= - # edc.core.system.health.check.readiness-period= - # edc.core.system.health.check.startup-period= - # edc.core.system.health.check.threadpool-size= - # edc.dataplane.queue.capacity= - # edc.dataplane.wait= - # edc.dataplane.workers= - # edc.datasource.asset.name="default" - # edc.datasource.contractdefinition.name="default" - # edc.datasource.contractnegotiation.name="default" - # edc.datasource.policy.name="default" - # edc.datasource.transferprocess.name="default" - # edc.datasource.default.pool.maxIdleConnections= - # edc.datasource.default.pool.maxTotalConnections= - # edc.datasource.default.pool.minIdleConnections= - # edc.datasource.default.pool.testConnectionOnBorrow= - # edc.datasource.default.pool.testConnectionOnCreate= - # edc.datasource.default.pool.testConnectionOnReturn= - # edc.datasource.default.pool.testConnectionWhileIdle= - # edc.datasource.default.pool.testQuery= - # edc.datasource.default.url= - # edc.datasource.default.user= - # edc.datasource.default.password= - # edc.dpf.selector.url= - # edc.events.topic.endpoint= - # edc.events.topic.name= - # edc.fs.config= - # edc.hostname= - # edc.identity.did.url= - # edc.ids.catalog.id= - # edc.ids.curator= - # edc.ids.description= - # edc.ids.endpoint= - # edc.ids.id= - # edc.ids.maintainer= - # edc.ids.security.profile= - # edc.ids.title= - # edc.ids.validation.referringconnector= - # edc.ion.crawler.did-type= - # edc.ion.crawler.interval-minutes= - # edc.ion.crawler.ion.url= - # edc.metrics.enabled= - # edc.metrics.executor.enabled= - # edc.metrics.jersey.enabled= - # edc.metrics.jetty.enabled= - # edc.metrics.okhttp.enabled= - # edc.metrics.system.enabled= - # edc.negotiation.consumer.state-machine.batch-size= - # edc.negotiation.provider.state-machine.batch-size= - # edc.oauth.client.id= - # edc.oauth.private.key.alias= - # edc.oauth.provider.audience= - # edc.oauth.provider.jwks.refresh= - # edc.oauth.provider.jwks.url= - # edc.oauth.public.key.alias= - # edc.oauth.token.url= - # edc.oauth.validation.nbf.leeway= - # edc.receiver.http.auth-code= - # edc.receiver.http.auth-key= - # edc.receiver.http.endpoint= - # edc.transfer.proxy.endpoint= - # edc.transfer.proxy.token.validity.seconds= - # edc.transfer.proxy.token.signer.privatekey.alias= - # edc.transfer.functions.check.endpoint= - # edc.transfer.functions.enabled.protocols= - # edc.transfer.functions.transfer.endpoint= - # edc.transfer-process-store.database.name= - # edc.transfer.state-machine.batch-size= - # edc.vault= - # edc.vault.certificate= - # edc.vault.clientid= - # edc.vault.clientsecret= - # edc.vault.name= - # edc.vault.tenantid= - # edc.vault.hashicorp.url= - # edc.vault.hashicorp.token= - # edc.vault.hashicorp.timeout.seconds= - # edc.webdid.doh.url= - # edc.web.rest.cors.enabled= - # edc.web.rest.cors.headers= - # edc.web.rest.cors.methods= - # edc.web.rest.cors.origins= - # ids.webhook.address= diff --git a/charts/edc-dataplane/.helmignore b/charts/edc-dataplane/.helmignore deleted file mode 100644 index 148b31d6c..000000000 --- a/charts/edc-dataplane/.helmignore +++ /dev/null @@ -1,29 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ - -README.md.gotmpl - -# Accept only values.yaml -values?*.yaml -values?*.yml diff --git a/charts/edc-dataplane/Chart.yaml b/charts/edc-dataplane/Chart.yaml deleted file mode 100644 index 96d5598fa..000000000 --- a/charts/edc-dataplane/Chart.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v2 -name: edc-dataplane -description: >- - EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams -home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-dataplane -type: application -appVersion: "0.3.2" -version: 0.3.2 -deprecated: true -maintainers: [] -sources: - - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-dataplane diff --git a/charts/edc-dataplane/LICENSE b/charts/edc-dataplane/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/charts/edc-dataplane/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/charts/edc-dataplane/README.md b/charts/edc-dataplane/README.md deleted file mode 100644 index 6085ccbbc..000000000 --- a/charts/edc-dataplane/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# edc-dataplane - -> **:exclamation: This Helm Chart is deprecated!** - -![Version: 0.3.2](https://img.shields.io/badge/Version-0.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.2](https://img.shields.io/badge/AppVersion-0.3.2-informational?style=flat-square) - -EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams - -**Homepage:** - -## TL;DR - -```shell -helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-dataplane --version 0.3.2 -``` - -## Source Code - -* - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | -| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | -| autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | -| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.dataplane.token.validation.endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.endpoint.audience=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-edc/Connector) | -| customLabels | object | `{}` | Additional custom Labels to add | -| edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | -| edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | -| edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | -| edc.endpoints.default.port | string | `"8080"` | The network port, which the "default" api is going to be exposed by the container, pod and service | -| edc.endpoints.metrics.path | string | `"/metrics"` | The path mapping the prometheus metrics are going to be exposed at | -| edc.endpoints.metrics.port | string | `"9090"` | The network port, which the prometheus metrics are going to be exposed by the container, pod and service | -| edc.endpoints.public.path | string | `"/api/public"` | The path mapping the "public" api is going to be exposed by | -| edc.endpoints.public.port | string | `"8185"` | The network port, which the "public" api is going to be exposed by the container, pod and service | -| env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | -| envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | -| fullnameOverride | string | `""` | Overrides the releases full name | -| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-azure-vault] | -| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | -| imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | -| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | -| ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | -| ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| ingresses[0].enabled | bool | `true` | | -| ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | -| ingresses[0].hostname | string | `"edc-dataplane.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | -| livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| logging.properties | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) | -| nameOverride | string | `""` | Overrides the charts name | -| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | -| opentelemetry.properties | string | `"otel.javaagent.enabled=true\notel.javaagent.debug=false"` | opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) | -| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | -| podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | -| podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | -| podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | -| podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | -| readinessProbe.enabled | bool | `true` | Whether to enable kubernetes readiness-probes | -| replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | -| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | -| securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | -| securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | -| securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | -| securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | -| securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | -| service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | -| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | -| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | -| startupProbe.enabled | bool | `true` | Whether to enable kubernetes startup-probes | -| startupProbe.failureThreshold | int | `12` | Minimum consecutive failures for the probe to be considered failed after having succeeded | -| startupProbe.initialDelaySeconds | int | `10` | Number of seconds after the container has started before liveness probes are initiated. | -| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | - ----------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/edc-dataplane/README.md.gotmpl b/charts/edc-dataplane/README.md.gotmpl deleted file mode 100644 index c94d26d50..000000000 --- a/charts/edc-dataplane/README.md.gotmpl +++ /dev/null @@ -1,26 +0,0 @@ -{{ template "chart.header" . }} - -{{ template "chart.deprecationWarning" . }} - -{{ template "chart.badgesSection" . }} - -{{ template "chart.description" . }} - -{{ template "chart.homepageLine" . }} - -## TL;DR - -```shell -helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-dataplane --version {{ .Version }} -``` - -{{ template "chart.maintainersSection" . }} - -{{ template "chart.sourcesSection" . }} - -{{ template "chart.requirementsSection" . }} - -{{ template "chart.valuesSection" . }} - -{{ template "helm-docs.versionFooter" . }} diff --git a/charts/edc-dataplane/templates/NOTES.txt b/charts/edc-dataplane/templates/NOTES.txt deleted file mode 100644 index 454b250eb..000000000 --- a/charts/edc-dataplane/templates/NOTES.txt +++ /dev/null @@ -1,64 +0,0 @@ - -CHART NAME: {{ .Chart.Name }} -CHART VERSION: {{ .Chart.Version }} -APP VERSION: {{ .Chart.AppVersion }} - -Logs can be accessed by running this command: - - kubectl logs --tail 100 -f \ - --namespace {{ .Release.Namespace }} \ - -l "app.kubernetes.io/name={{ include "edc-dataplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" - -{{- if .Values.ingresses }} - -Following ingress URLS are available: - {{- $edcEndpoints := .Values.edc.endpoints }} - {{- range .Values.ingresses }} - {{- if .enabled }} - {{- $ingressEdcEndpoints := .endpoints }} - {{- $hostname := .hostname }} - {{- $tls := .tls }} - {{- range $name, $mapping := $edcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - Visit http{{ if $tls }}s{{ end }}://{{ $hostname }}{{ $mapping.path }} to access the {{ $name }} api - {{- end }} - {{- end }} - {{- end }} - {{- end }} - -{{- else if contains "NodePort" .Values.service.type }} -Get the application URLs by running these commands: - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - - export NODE_PORT_DEFAULT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") - export NODE_PORT_PUBLIC=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") - export NODE_PORT_CONTROL=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[2].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") - export NODE_PORT_METRICS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[3].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") - - echo "Visit http://$NODE_IP:$NODE_PORT_DEFAULT to access the default api" - echo "Visit http://$NODE_IP:$NODE_PORT_PUBLIC to access the public data transfer api" - echo "Visit http://$NODE_IP:$NODE_PORT_CONTROL to access the control api" - echo "Visit http://$NODE_IP:$NODE_PORT_METRICS to access the metrics api" - -{{- else if contains "ClusterIP" .Values.service.type }} -Get the application URL by running these commands: - - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "edc-dataplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - - export CONTAINER_PORT_DEFAULT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - export CONTAINER_PORT_PUBLIC=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[1].containerPort}") - export CONTAINER_PORT_CONTROL=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[2].containerPort}") - export CONTAINER_PORT_METRICS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[3].containerPort}") - - echo "Visit http://127.0.0.1:8080 to access the default api" - echo "Visit http://127.0.0.1:8185 to access the public data transfer api" - echo "Visit http://127.0.0.1:9999 to access the control api" - echo "Visit http://127.0.0.1:9090 to access the metrics api" - - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME \ - 8080:$CONTAINER_PORT_DEFAULT \ - 8185:$CONTAINER_PORT_PUBLIC \ - 9999:$CONTAINER_PORT_CONTROL \ - 9090:$CONTAINER_PORT_METRICS - -{{- end }} diff --git a/charts/edc-dataplane/templates/_helpers.tpl b/charts/edc-dataplane/templates/_helpers.tpl deleted file mode 100644 index 3615298cd..000000000 --- a/charts/edc-dataplane/templates/_helpers.tpl +++ /dev/null @@ -1,72 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "edc-dataplane.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "edc-dataplane.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "edc-dataplane.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "edc-dataplane.labels" -}} -helm.sh/chart: {{ include "edc-dataplane.chart" . }} -{{ include "edc-dataplane.selectorLabels" . }} -{{ include "edc-dataplane.customLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "edc-dataplane.selectorLabels" -}} -app.kubernetes.io/name: {{ include "edc-dataplane.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Custom labels -*/}} -{{- define "edc-dataplane.customLabels" -}} -{{- with .Values.customLabels }} -{{ toYaml . }} -{{- end }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "edc-dataplane.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "edc-dataplane.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/charts/edc-dataplane/templates/configmap-env.yaml b/charts/edc-dataplane/templates/configmap-env.yaml deleted file mode 100644 index 0e021734a..000000000 --- a/charts/edc-dataplane/templates/configmap-env.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "edc-dataplane.fullname" . }}-env - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -data: - {{- toYaml .Values.env | nindent 2 }} diff --git a/charts/edc-dataplane/templates/configmap.yaml b/charts/edc-dataplane/templates/configmap.yaml deleted file mode 100644 index c7daa322f..000000000 --- a/charts/edc-dataplane/templates/configmap.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "edc-dataplane.fullname" . }}-configmap - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -data: - configuration.properties: |- - web.http.default.port={{ .Values.edc.endpoints.default.port }} - web.http.default.path={{ .Values.edc.endpoints.default.path }} - web.http.public.port={{ .Values.edc.endpoints.public.port }} - web.http.public.path={{ .Values.edc.endpoints.public.path }} - web.http.control.port={{ .Values.edc.endpoints.control.port }} - web.http.control.path={{ .Values.edc.endpoints.control.path }} - {{- .Values.configuration.properties | nindent 4 }} - - opentelemetry.properties: |- - {{- .Values.opentelemetry.properties | nindent 4 }} - - logging.properties: |- - {{- .Values.logging.properties | nindent 4 }} diff --git a/charts/edc-dataplane/templates/deployment.yaml b/charts/edc-dataplane/templates/deployment.yaml deleted file mode 100644 index 474b04650..000000000 --- a/charts/edc-dataplane/templates/deployment.yaml +++ /dev/null @@ -1,142 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "edc-dataplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "edc-dataplane.selectorLabels" . | nindent 6 }} - template: - metadata: - annotations: - checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - checksum/env-config: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} - {{- with .Values.podAnnotations }} - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "edc-dataplane.selectorLabels" . | nindent 8 }} - spec: - {{- if .Values.imagePullSecret.dockerconfigjson }} - imagePullSecrets: - - name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret - {{- else }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- end }} - serviceAccountName: {{ include "edc-dataplane.serviceAccountName" . }} - automountServiceAccountToken: {{ if .Values.automountServiceAccountToken }}true{{ else }}false{{ end }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: default - containerPort: {{ .Values.edc.endpoints.default.port }} - protocol: TCP - - name: public - containerPort: {{ .Values.edc.endpoints.public.port }} - protocol: TCP - - name: control - containerPort: {{ .Values.edc.endpoints.control.port }} - protocol: TCP - - name: metrics - containerPort: {{ .Values.edc.endpoints.metrics.port }} - protocol: TCP - {{- if .Values.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/liveness - port: default - {{- end }} - {{- if .Values.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/readiness - port: default - {{- end }} - {{- if .Values.startupProbe.enabled }} - startupProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/startup - port: default - failureThreshold: {{ .Values.startupProbe.failureThreshold }} - initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} - {{- end }} - envFrom: - - configMapRef: - name: {{ include "edc-dataplane.fullname" . }}-env - {{- if .Values.envSecretName }} - - secretRef: - name: {{ .Values.envSecretName | quote }} - {{- end }} - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: configuration - mountPath: /app/configuration.properties - subPath: configuration.properties - - name: configuration - mountPath: /app/opentelemetry.properties - subPath: opentelemetry.properties - - name: configuration - mountPath: /app/logging.properties - subPath: logging.properties - volumes: - - name: configuration - configMap: - name: {{ include "edc-dataplane.fullname" . }}-configmap - items: - - key: configuration.properties - path: configuration.properties - - key: opentelemetry.properties - path: opentelemetry.properties - - key: logging.properties - path: logging.properties - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/charts/edc-dataplane/templates/hpa.yaml b/charts/edc-dataplane/templates/hpa.yaml deleted file mode 100644 index 037934aeb..000000000 --- a/charts/edc-dataplane/templates/hpa.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.autoscaling.enabled }} ---- -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "edc-dataplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "edc-dataplane.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/edc-dataplane/templates/imagepullsecret.yaml b/charts/edc-dataplane/templates/imagepullsecret.yaml deleted file mode 100644 index 11961674b..000000000 --- a/charts/edc-dataplane/templates/imagepullsecret.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.imagePullSecret.dockerconfigjson }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -data: - .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} -type: kubernetes.io/dockerconfigjson -{{- end }} diff --git a/charts/edc-dataplane/templates/ingress.yaml b/charts/edc-dataplane/templates/ingress.yaml deleted file mode 100644 index 716ac3d1f..000000000 --- a/charts/edc-dataplane/templates/ingress.yaml +++ /dev/null @@ -1,100 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- $fullName := include "edc-dataplane.fullname" . }} -{{- $labels := include "edc-dataplane.labels" . | nindent 4 }} -{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} -{{- $edcEndpoints := .Values.edc.endpoints }} -{{- $namespace := .Release.Namespace }} -{{- range .Values.ingresses }} -{{- if and .enabled .endpoints }} -{{- $ingressName := printf "%s-%s" $fullName .hostname }} ---- -{{- if semverCompare ">=1.19-0" $gitVersion }} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" $gitVersion }} -apiVersion: networking.k8s.io/v1beta1 -{{- else }} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $ingressName }} - namespace: {{ $namespace | default "default" | quote }} - labels: - {{- $labels | nindent 2 }} - annotations: - {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} - {{- if not (hasKey .annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .annotations "kubernetes.io/ingress.class" .className}} - {{- end }} - {{- end }} - {{- if .certManager }} - {{- if .certManager.issuer }} - {{- $_ := set .annotations "cert-manager.io/issuer" .certManager.issuer}} - {{- end }} - {{- if .certManager.clusterIssuer }} - {{- $_ := set .annotations "cert-manager.io/cluster-issuer" .certManager.clusterIssuer}} - {{- end }} - {{- end }} - {{- with .annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .className (semverCompare ">=1.18-0" $gitVersion) }} - ingressClassName: {{ .className }} - {{- end }} - {{- if .hostname }} - {{- if .tls.enabled }} - tls: - - hosts: - - {{ .hostname }} - {{- if .tls.secretName }} - secretName: {{ .tls.secretName }} - {{- else }} - secretName: {{ $ingressName }}-tls - {{- end }} - {{- end }} - rules: - - host: {{ .hostname }} - http: - paths: - {{- $ingressEdcEndpoints := .endpoints }} - {{- range $name, $mapping := $edcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - - path: {{ $mapping.path }} - pathType: Prefix - backend: - {{- if semverCompare ">=1.19-0" $gitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $mapping.port }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $mapping.port }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} -{{- end }}{{- /* end: if .enabled */}} -{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/charts/edc-dataplane/templates/service.yaml b/charts/edc-dataplane/templates/service.yaml deleted file mode 100644 index e4d081776..000000000 --- a/charts/edc-dataplane/templates/service.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "edc-dataplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.edc.endpoints.default.port }} - targetPort: default - protocol: TCP - name: default - - port: {{ .Values.edc.endpoints.control.port }} - targetPort: control - protocol: TCP - name: control - - port: {{ .Values.edc.endpoints.public.port }} - targetPort: public - protocol: TCP - name: public - - port: {{ .Values.edc.endpoints.metrics.port }} - targetPort: metrics - protocol: TCP - name: metrics - selector: - {{- include "edc-dataplane.selectorLabels" . | nindent 4 }} diff --git a/charts/edc-dataplane/templates/serviceaccount.yaml b/charts/edc-dataplane/templates/serviceaccount.yaml deleted file mode 100644 index 39a44d35e..000000000 --- a/charts/edc-dataplane/templates/serviceaccount.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.serviceAccount.create -}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "edc-dataplane.serviceAccountName" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/charts/edc-dataplane/values.yaml b/charts/edc-dataplane/values.yaml deleted file mode 100644 index 9a049cb1f..000000000 --- a/charts/edc-dataplane/values.yaml +++ /dev/null @@ -1,331 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -# Default values for edc-dataplane. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -# -- Specifies how many replicas of a deployed pod shall be created during the deployment -# Note: If horizontal pod autoscaling is enabled this setting has no effect -replicaCount: 1 - -image: - # -- Which derivate of the edc data-plane to use. - # One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-azure-vault] - repository: ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion - tag: "" - -imagePullSecret: - # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) - # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). - # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. - dockerconfigjson: "" - -# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) -imagePullSecrets: [] - -# -- Overrides the charts name -nameOverride: "" - -# -- Overrides the releases full name -fullnameOverride: "" - -# -- Additional custom Labels to add -customLabels: {} - -serviceAccount: - # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release - create: true - # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account - annotations: {} - # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template - name: "" - -# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod -automountServiceAccountToken: false - -# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) -podAnnotations: {} - -# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment -podSecurityContext: - seccompProfile: - # -- Restrict a Container's Syscalls with seccomp - type: RuntimeDefault - # -- Runs all processes within a pod with a special uid - runAsUser: 10001 - # -- Processes within a pod will belong to this guid - runAsGroup: 10001 - # -- The owner for volumes and any files created within volumes will belong to this guid - fsGroup: 10001 - -# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod -securityContext: - capabilities: - # -- Specifies which capabilities to drop to reduce syscall attack surface - drop: - - ALL - # -- Specifies which capabilities to add to issue specialized syscalls - add: [] - # -- Whether the root filesystem is mounted in read-only mode - readOnlyRootFilesystem: true - # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID - allowPrivilegeEscalation: false - # -- Requires the container to run without root privileges - runAsNonRoot: true - # -- The container's process will run with the specified uid - runAsUser: 10001 - -livenessProbe: - # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - -readinessProbe: - # -- Whether to enable kubernetes readiness-probes - enabled: true - -startupProbe: - # -- Whether to enable kubernetes startup-probes - enabled: true - # -- Minimum consecutive failures for the probe to be considered failed after having succeeded - failureThreshold: 12 - # -- Number of seconds after the container has started before liveness probes are initiated. - initialDelaySeconds: 10 - -## EDC endpoints exposed by the data-plane -edc: - endpoints: - ## Default api exposing health checks etc - default: - # -- The network port, which the "default" api is going to be exposed by the container, pod and service - port: "8080" - # -- The path mapping the "default" api is going to be exposed by - path: /api - ## Public endpoint for data transfer - public: - # -- The network port, which the "public" api is going to be exposed by the container, pod and service - port: "8185" - # -- The path mapping the "public" api is going to be exposed by - path: /api/public - ## Control API - control: - # -- The network port, which the "control" api is going to be exposed by the container, pod and service - port: "9999" - # -- The path mapping the "control" api is going to be exposed by - path: /api/dataplane/control - ## Prometheus endpoint - metrics: - # -- The network port, which the prometheus metrics are going to be exposed by the container, pod and service - port: "9090" - # -- The path mapping the prometheus metrics are going to be exposed at - path: /metrics - -service: - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. - type: ClusterIP - -## Ingress declaration to expose the network service. -ingresses: - ## Public / Internet facing Ingress - - enabled: true - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-dataplane.local" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - public - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - -# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod -resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - -# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. -nodeSelector: {} - -# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. -tolerations: [] - -# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. -affinity: {} - -# -- Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) -# Ex.: -# JAVA_TOOL_OPTIONS: > -# -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 -env: {} - -# -- [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from -envSecretName: - -logging: - # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) - properties: |- - .level=INFO - org.eclipse.edc.level=ALL - handlers=java.util.logging.ConsoleHandler - java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter - java.util.logging.ConsoleHandler.level=ALL - java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n - -opentelemetry: - # -- opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) - properties: |- - otel.javaagent.enabled=true - otel.javaagent.debug=false - -configuration: - # -- EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-edc/Connector) - properties: |- - # edc.atomikos.checkpoint.interval= - # edc.atomikos.directory= - # edc.atomikos.logging= - # edc.atomikos.threaded2pc= - # edc.atomikos.timeout= - # edc.aws.access.key= - # edc.aws.provision.retry.retries.max= - # edc.aws.provision.role.duration.session.max= - # edc.aws.secret.access.key= - # edc.blobstore.endpoint= - # edc.dataplane.token.validation.endpoint= - # edc.core.retry.backoff.max= - # edc.core.retry.backoff.min= - # edc.core.retry.retries.max= - # edc.core.system.health.check.liveness-period= - # edc.core.system.health.check.readiness-period= - # edc.core.system.health.check.startup-period= - # edc.core.system.health.check.threadpool-size= - # edc.dataplane.queue.capacity= - # edc.dataplane.wait= - # edc.dataplane.workers= - # edc.datasource.asset.name="default" - # edc.datasource.contractdefinition.name="default" - # edc.datasource.contractnegotiation.name="default" - # edc.datasource.policy.name="default" - # edc.datasource.transferprocess.name="default" - # edc.datasource.default.pool.maxIdleConnections= - # edc.datasource.default.pool.maxTotalConnections= - # edc.datasource.default.pool.minIdleConnections= - # edc.datasource.default.pool.testConnectionOnBorrow= - # edc.datasource.default.pool.testConnectionOnCreate= - # edc.datasource.default.pool.testConnectionOnReturn= - # edc.datasource.default.pool.testConnectionWhileIdle= - # edc.datasource.default.pool.testQuery= - # edc.datasource.default.url= - # edc.datasource.default.user= - # edc.datasource.default.password= - # edc.dpf.selector.url= - # edc.events.topic.endpoint= - # edc.events.topic.name= - # edc.fs.config= - # edc.hostname= - # edc.identity.did.url= - # edc.ids.catalog.id= - # edc.ids.curator= - # edc.ids.description= - # edc.ids.endpoint= - # edc.ids.endpoint.audience= - # edc.ids.id= - # edc.ids.maintainer= - # edc.ids.security.profile= - # edc.ids.title= - # edc.ids.validation.referringconnector= - # edc.ion.crawler.did-type= - # edc.ion.crawler.interval-minutes= - # edc.ion.crawler.ion.url= - # edc.metrics.enabled= - # edc.metrics.executor.enabled= - # edc.metrics.jersey.enabled= - # edc.metrics.jetty.enabled= - # edc.metrics.okhttp.enabled= - # edc.metrics.system.enabled= - # edc.negotiation.consumer.state-machine.batch-size= - # edc.negotiation.provider.state-machine.batch-size= - # edc.oauth.client.id= - # edc.oauth.private.key.alias= - # edc.oauth.provider.jwks.refresh= - # edc.oauth.provider.jwks.url= - # edc.oauth.public.key.alias= - # edc.oauth.token.url= - # edc.oauth.validation.nbf.leeway= - # edc.receiver.http.auth-code= - # edc.receiver.http.auth-key= - # edc.receiver.http.endpoint= - # edc.transfer.functions.check.endpoint= - # edc.transfer.functions.enabled.protocols= - # edc.transfer.functions.transfer.endpoint= - # edc.transfer-process-store.database.name= - # edc.transfer.state-machine.batch-size= - # edc.vault= - # edc.vault.certificate= - # edc.vault.clientid= - # edc.vault.clientsecret= - # edc.vault.name= - # edc.vault.tenantid= - # edc.vault.hashicorp.url= - # edc.vault.hashicorp.token= - # edc.vault.hashicorp.timeout.seconds= - # edc.webdid.doh.url= - # edc.web.rest.cors.enabled= - # edc.web.rest.cors.headers= - # edc.web.rest.cors.methods= - # edc.web.rest.cors.origins= From d916e9e8d7682d265a9e95cecd4e6c507243904b Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Tue, 4 Apr 2023 09:51:50 +0200 Subject: [PATCH 036/132] Update docs/development/decision-records/2023-04-03_renaming_branches/README.md Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- .../decision-records/2023-04-03_renaming_branches/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/decision-records/2023-04-03_renaming_branches/README.md b/docs/development/decision-records/2023-04-03_renaming_branches/README.md index dcb80865c..5fc2fe4e5 100644 --- a/docs/development/decision-records/2023-04-03_renaming_branches/README.md +++ b/docs/development/decision-records/2023-04-03_renaming_branches/README.md @@ -48,7 +48,7 @@ fork" refers to `catenax-ng/tx-tractusx-edc`. - push the contents of `fork/main` -> `upstream/releases` - synchronize `upstream/develop` with `fork/develop` - force-push the contents of `develop` -> `upstream/main` (do **not** update the tracking branch!) -- synchronize `upstream/main` -> `fork/main`. +- synchronize `upstream/main` -> `fork/main` - delete/archive `upstream/develop` and `fork/develop` _Note that most of this will likely need to be done manually, since GitHub does not allow for advanced Git operations From 70a1ac4a30455b3c274ac42853e78dd1ac029d12 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Tue, 4 Apr 2023 09:51:57 +0200 Subject: [PATCH 037/132] Update docs/development/decision-records/2023-04-03_renaming_branches/README.md Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- .../decision-records/2023-04-03_renaming_branches/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/decision-records/2023-04-03_renaming_branches/README.md b/docs/development/decision-records/2023-04-03_renaming_branches/README.md index 5fc2fe4e5..7bc3abd64 100644 --- a/docs/development/decision-records/2023-04-03_renaming_branches/README.md +++ b/docs/development/decision-records/2023-04-03_renaming_branches/README.md @@ -44,7 +44,7 @@ section outlines the exact sequence of steps. Note that "upstream" refers to `ec fork" refers to `catenax-ng/tx-tractusx-edc`. - create a new branch `upstream/releases` -- create a new branch `fork/releaes`, set it to track `upstream/releases` +- create a new branch `fork/releases`, set it to track `upstream/releases` - push the contents of `fork/main` -> `upstream/releases` - synchronize `upstream/develop` with `fork/develop` - force-push the contents of `develop` -> `upstream/main` (do **not** update the tracking branch!) From 01d6cf92be88554d6ac20c34026ba08d4f772136 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Tue, 4 Apr 2023 16:04:56 +0200 Subject: [PATCH 038/132] feat(dataEncryption): removes lombok from data-encryption module --- .../algorithms/aes/AesAlgorithm.java | 132 +++++++++-------- .../aes/AesInitializationVectorIterator.java | 63 ++++---- .../algorithms/aes/ByteCounter.java | 102 +++++++------ .../data/CryptoDataFactoryImpl.java | 131 ++++++++++------- .../AesDataEncrypterConfiguration.java | 28 +++- .../encrypter/AesDataEncrypterImpl.java | 137 +++++++++--------- .../encrypter/DataEncrypterFactory.java | 74 +++++----- .../encryption/key/CryptoKeyFactoryImpl.java | 22 ++- .../encryption/provider/AesKeyProvider.java | 73 +++++----- .../provider/CachingKeyProvider.java | 105 ++++++++------ .../algorithms/aes/AesAlgorithmTest.java | 126 ++++++++-------- .../AesInitializationVectorIteratorTest.java | 80 +++++----- .../DataEncrypterAesComponentTest.java | 125 ++++++++-------- 13 files changed, 649 insertions(+), 549 deletions(-) diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java index 69c54a173..6f463fc82 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java @@ -20,93 +20,91 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; +import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; +import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; + import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.Objects; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; -import lombok.NonNull; -import lombok.SneakyThrows; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; -import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; -import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; -import org.jetbrains.annotations.NotNull; public class AesAlgorithm implements CryptoAlgorithm { - private static final String AES_GCM = "AES/GCM/NoPadding"; - private static final String AES = "AES"; - private static final Object MONITOR = new Object(); + private static final String AES_GCM = "AES/GCM/NoPadding"; + private static final String AES = "AES"; + private static final Object MONITOR = new Object(); + + private final SecureRandom secureRandom; - private final SecureRandom secureRandom; + private final CryptoDataFactory cryptoDataFactory; + private AesInitializationVectorIterator initializationVectorIterator; - @NonNull private final CryptoDataFactory cryptoDataFactory; - private AesInitializationVectorIterator initializationVectorIterator; + public AesAlgorithm(CryptoDataFactory cryptoDataFactory) { + this.cryptoDataFactory = Objects.requireNonNull(cryptoDataFactory); - @SneakyThrows - public AesAlgorithm(@NotNull CryptoDataFactory cryptoDataFactory) { - this.cryptoDataFactory = cryptoDataFactory; + // We use new SecureRandom() and not SecureRandom.getInstanceStrong(), as the second one + // would use a blocking algorithm, which leads to an increased encryption time of up to 3 + // minutes. Since we have already used /dev/urandom, which only provides pseudo-randomness and + // is also non-blocking, switching to a non-blocking algorithm should not matter here either. + this.secureRandom = new SecureRandom(); + this.initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); + } - // We use new SecureRandom() and not SecureRandom.getInstanceStrong(), as the second one - // would use a blocking algorithm, which leads to an increased encryption time of up to 3 - // minutes. Since we have already used /dev/urandom, which only provides pseudo-randomness and - // is also non-blocking, switching to a non-blocking algorithm should not matter here either. - this.secureRandom = new SecureRandom(); - this.initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); - } + @Override + public synchronized EncryptedData encrypt(DecryptedData data, AesKey key) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - @Override - public synchronized EncryptedData encrypt(DecryptedData data, AesKey key) - throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { + final byte[] initializationVector; + synchronized (MONITOR) { + if (!initializationVectorIterator.hasNext()) { + initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); + } - final byte[] initializationVector; - synchronized (MONITOR) { - if (!initializationVectorIterator.hasNext()) { - initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); - } + initializationVector = initializationVectorIterator.next(); + } - initializationVector = initializationVectorIterator.next(); + Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); + final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); + final GCMParameterSpec gcmParameterSpec = + new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec); + byte[] encrypted = cipher.doFinal(data.getBytes()); + byte[] encryptedWithVector = ArrayUtil.concat(initializationVector, encrypted); + + return cryptoDataFactory.encryptedFromBytes(encryptedWithVector); } - Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); - final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); - final GCMParameterSpec gcmParameterSpec = - new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); - cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec); - byte[] encrypted = cipher.doFinal(data.getBytes()); - byte[] encryptedWithVector = ArrayUtil.concat(initializationVector, encrypted); - - return cryptoDataFactory.encryptedFromBytes(encryptedWithVector); - } - - @Override - public DecryptedData decrypt(EncryptedData data, AesKey key) - throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - byte[] encryptedWithVector = data.getBytes(); - byte[] initializationVector = ArrayUtil.subArray(encryptedWithVector, 0, 16); - byte[] encrypted = ArrayUtil.subArray(encryptedWithVector, 16, encryptedWithVector.length - 16); - - Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); - final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); - final GCMParameterSpec gcmParameterSpec = - new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); - cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); - byte[] decryptedData = cipher.doFinal(encrypted); - return cryptoDataFactory.decryptedFromBytes(decryptedData); - } - - public String getAlgorithm() { - return this.secureRandom.getAlgorithm(); - } + @Override + public DecryptedData decrypt(EncryptedData data, AesKey key) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { + byte[] encryptedWithVector = data.getBytes(); + byte[] initializationVector = ArrayUtil.subArray(encryptedWithVector, 0, 16); + byte[] encrypted = ArrayUtil.subArray(encryptedWithVector, 16, encryptedWithVector.length - 16); + + Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); + final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); + final GCMParameterSpec gcmParameterSpec = + new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); + cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); + byte[] decryptedData = cipher.doFinal(encrypted); + return cryptoDataFactory.decryptedFromBytes(decryptedData); + } + + public String getAlgorithm() { + return this.secureRandom.getAlgorithm(); + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java index cd0a6b1ec..73d02c3d5 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java @@ -20,51 +20,50 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; +import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; + import java.security.SecureRandom; import java.util.Iterator; import java.util.NoSuchElementException; -import lombok.SneakyThrows; -import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; public class AesInitializationVectorIterator implements Iterator { - public static final int RANDOM_SIZE = 12; - public static final int COUNTER_SIZE = 4; - - private final ByteCounter counter; + public static final int RANDOM_SIZE = 12; + public static final int COUNTER_SIZE = 4; - private SecureRandom secureRandom; + private final ByteCounter counter; - public AesInitializationVectorIterator(SecureRandom secureRandom) { - this.counter = new ByteCounter(COUNTER_SIZE); - this.secureRandom = secureRandom; - } + private SecureRandom secureRandom; - public AesInitializationVectorIterator(ByteCounter byteCounter) { - this.counter = byteCounter; - } + public AesInitializationVectorIterator(SecureRandom secureRandom) { + this.counter = new ByteCounter(COUNTER_SIZE); + this.secureRandom = secureRandom; + } - @Override - public boolean hasNext() { - return !counter.isMaxed(); - } + public AesInitializationVectorIterator(ByteCounter byteCounter) { + this.counter = byteCounter; + } - @Override - public byte[] next() { - if (counter.isMaxed()) { - throw new NoSuchElementException(getClass().getSimpleName() + " has no more elements"); + @Override + public boolean hasNext() { + return !counter.isMaxed(); } - byte[] random = getNextRandom(); - counter.increment(); + @Override + public byte[] next() { + if (counter.isMaxed()) { + throw new NoSuchElementException(getClass().getSimpleName() + " has no more elements"); + } - return ArrayUtil.concat(random, counter.getBytes()); - } + byte[] random = getNextRandom(); + counter.increment(); - @SneakyThrows - public byte[] getNextRandom() { - byte[] newVector = new byte[RANDOM_SIZE]; - secureRandom.nextBytes(newVector); - return newVector; - } + return ArrayUtil.concat(random, counter.getBytes()); + } + + public byte[] getNextRandom() { + byte[] newVector = new byte[RANDOM_SIZE]; + secureRandom.nextBytes(newVector); + return newVector; + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java index 55eec8184..e7874c158 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java @@ -19,63 +19,69 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; -/** Big Endian Byte Counter */ +/** + * Big Endian Byte Counter + */ public class ByteCounter { - private final byte[] counter; - - /** - * Constructs a new ByteCounter with the given number of bytes. E.g. a ByteCounter with 4 bytes - * will have a counter value of [0, 0, 0, 0]. - * - * @param size number of bytes used by the counter - */ - public ByteCounter(int size) { - this.counter = new byte[size]; - } + private final byte[] counter; - /** - * Constructs a new ByteCounter with the given counter value. Counter cannot grow bigger than the - * size of the array. - * - * @param counter initial counter value - */ - public ByteCounter(byte[] counter) { - this.counter = counter; - } + /** + * Constructs a new ByteCounter with the given number of bytes. E.g. a ByteCounter with 4 bytes + * will have a counter value of [0, 0, 0, 0]. + * + * @param size number of bytes used by the counter + */ + public ByteCounter(int size) { + this.counter = new byte[size]; + } - /** Returns the counter value as a byte array. */ - public byte[] getBytes() { - return counter; - } + /** + * Constructs a new ByteCounter with the given counter value. Counter cannot grow bigger than the + * size of the array. + * + * @param counter initial counter value + */ + public ByteCounter(byte[] counter) { + this.counter = counter; + } - /** Returns true if counter is maxed */ - public boolean isMaxed() { - for (byte b : counter) { - if (b != (byte) 0xff) return false; + /** + * Returns the counter value as a byte array. + */ + public byte[] getBytes() { + return counter; } - return true; - } - /** - * Increments the counter by one. - * - * @throws IllegalStateException if the counter is already maxed - */ - public void increment() { - incrementByte(counter.length - 1); - } + /** + * Returns true if counter is maxed + */ + public boolean isMaxed() { + for (byte b : counter) { + if (b != (byte) 0xff) return false; + } + return true; + } - private void incrementByte(int index) { - if (isMaxed()) { - throw new IllegalStateException("Counter is already maxed"); + /** + * Increments the counter by one. + * + * @throws IllegalStateException if the counter is already maxed + */ + public void increment() { + incrementByte(counter.length - 1); } - if (counter[index] == (byte) 0xff) { - incrementByte(index - 1); - counter[index] = (byte) 0x00; - } else { - counter[index]++; + private void incrementByte(int index) { + if (isMaxed()) { + throw new IllegalStateException("Counter is already maxed"); + } + + if (counter[index] == (byte) 0xff) { + incrementByte(index - 1); + counter[index] = (byte) 0x00; + } else { + counter[index]++; + } } - } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java index b23966170..a01331275 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java @@ -19,58 +19,89 @@ */ package org.eclipse.tractusx.edc.data.encryption.data; -import lombok.Value; import org.bouncycastle.util.encoders.Base64; public class CryptoDataFactoryImpl implements CryptoDataFactory { - public DecryptedData decryptedFromText(String text) { - final byte[] bytes = text.getBytes(); - final String base64 = Base64.toBase64String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public DecryptedData decryptedFromBase64(String base64) { - final byte[] bytes = Base64.decode(base64); - final String text = new String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public DecryptedData decryptedFromBytes(byte[] bytes) { - final String base64 = Base64.toBase64String(bytes); - final String text = new String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromText(String text) { - final byte[] bytes = text.getBytes(); - final String base64 = Base64.toBase64String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromBase64(String base64) { - final byte[] bytes = Base64.decode(base64); - final String text = new String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromBytes(byte[] bytes) { - final String base64 = Base64.toBase64String(bytes); - final String text = new String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - @Value - private static class DecryptedDataImpl implements DecryptedData { - byte[] bytes; - String base64; - String text; - } - - @Value - private static class EncryptedDataImpl implements EncryptedData { - byte[] bytes; - String base64; - String text; - } + public DecryptedData decryptedFromText(String text) { + final byte[] bytes = text.getBytes(); + final String base64 = Base64.toBase64String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public DecryptedData decryptedFromBase64(String base64) { + final byte[] bytes = Base64.decode(base64); + final String text = new String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public DecryptedData decryptedFromBytes(byte[] bytes) { + final String base64 = Base64.toBase64String(bytes); + final String text = new String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromText(String text) { + final byte[] bytes = text.getBytes(); + final String base64 = Base64.toBase64String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromBase64(String base64) { + final byte[] bytes = Base64.decode(base64); + final String text = new String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromBytes(byte[] bytes) { + final String base64 = Base64.toBase64String(bytes); + final String text = new String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + + private static class DecryptedDataImpl implements DecryptedData { + private final byte[] bytes; + private final String base64; + private final String text; + + private DecryptedDataImpl(byte[] bytes, String base64, String text) { + this.bytes = bytes; + this.base64 = base64; + this.text = text; + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public String getBase64() { + return base64; + } + } + + + private static class EncryptedDataImpl implements EncryptedData { + private final byte[] bytes; + private final String base64; + private final String text; + + private EncryptedDataImpl(byte[] bytes, String base64, String text) { + this.bytes = bytes; + this.base64 = base64; + this.text = text; + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public String getBase64() { + return base64; + } + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java index 725828acc..0723306e4 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java @@ -21,12 +21,28 @@ package org.eclipse.tractusx.edc.data.encryption.encrypter; import java.time.Duration; -import lombok.NonNull; -import lombok.Value; -@Value + public class AesDataEncrypterConfiguration { - @NonNull String keySetAlias; - boolean cachingEnabled; - @NonNull Duration cachingDuration; + private final String keySetAlias; + private final boolean cachingEnabled; + private final Duration cachingDuration; + + public AesDataEncrypterConfiguration(String keySetAlias, boolean cachingEnabled, Duration cachingDuration) { + this.keySetAlias = keySetAlias; + this.cachingEnabled = cachingEnabled; + this.cachingDuration = cachingDuration; + } + + public Duration getCachingDuration() { + return cachingDuration; + } + + public boolean isCachingEnabled() { + return cachingEnabled; + } + + public String getKeySetAlias() { + return keySetAlias; + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java index 160f57df0..d8b4add87 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java @@ -20,15 +20,6 @@ package org.eclipse.tractusx.edc.data.encryption.encrypter; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Optional; -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; @@ -40,69 +31,85 @@ import org.eclipse.tractusx.edc.data.encryption.key.AesKey; import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; -@RequiredArgsConstructor +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Optional; +import javax.crypto.AEADBadTagException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + public class AesDataEncrypterImpl implements DataEncrypter { - private final CryptoAlgorithm encryptionStrategy; - private final Monitor monitor; - private final KeyProvider keyProvider; - private final CryptoAlgorithm algorithm; - private final CryptoDataFactory cryptoDataFactory; + private final CryptoAlgorithm encryptionStrategy; + private final Monitor monitor; + private final KeyProvider keyProvider; + private final CryptoAlgorithm algorithm; + private final CryptoDataFactory cryptoDataFactory; - @Override - public String encrypt(String value) { - DecryptedData decryptedData = cryptoDataFactory.decryptedFromText(value); - AesKey key = keyProvider.getEncryptionKey(); + public AesDataEncrypterImpl(CryptoAlgorithm encryptionStrategy, Monitor monitor, KeyProvider keyProvider, CryptoAlgorithm algorithm, CryptoDataFactory cryptoDataFactory) { + this.encryptionStrategy = encryptionStrategy; + this.monitor = monitor; + this.keyProvider = keyProvider; + this.algorithm = algorithm; + this.cryptoDataFactory = cryptoDataFactory; + } - try { - EncryptedData encryptedData = algorithm.encrypt(decryptedData, key); - return encryptedData.getBase64(); - } catch (IllegalBlockSizeException - | BadPaddingException - | InvalidKeyException - | InvalidAlgorithmParameterException - | NoSuchPaddingException - | NoSuchAlgorithmException e) { - throw new EdcException(e); + @Override + public String encrypt(String value) { + DecryptedData decryptedData = cryptoDataFactory.decryptedFromText(value); + AesKey key = keyProvider.getEncryptionKey(); + + try { + EncryptedData encryptedData = algorithm.encrypt(decryptedData, key); + return encryptedData.getBase64(); + } catch (IllegalBlockSizeException + | BadPaddingException + | InvalidKeyException + | InvalidAlgorithmParameterException + | NoSuchPaddingException + | NoSuchAlgorithmException e) { + throw new EdcException(e); + } } - } - @Override - public String decrypt(String value) { - EncryptedData encryptedData = cryptoDataFactory.encryptedFromBase64(value); + @Override + public String decrypt(String value) { + EncryptedData encryptedData = cryptoDataFactory.encryptedFromBase64(value); - return keyProvider - .getDecryptionKeySet() - .map(key -> decrypt(encryptedData, key)) - .filter(Optional::isPresent) - .map(Optional::get) - .map(DecryptedData::getBytes) - .map(String::new) - .findFirst() - .orElseThrow( - () -> - new EdcException( - DataEncryptionExtension.EXTENSION_NAME - + ": Failed to decrypt data. This can happen if the key set is empty, contains invalid keys, the decryption key rotated out of the key set or because the data was encrypted by a different algorithm.")); - } + return keyProvider + .getDecryptionKeySet() + .map(key -> decrypt(encryptedData, key)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(DecryptedData::getBytes) + .map(String::new) + .findFirst() + .orElseThrow( + () -> + new EdcException( + DataEncryptionExtension.EXTENSION_NAME + + ": Failed to decrypt data. This can happen if the key set is empty, contains invalid keys, the decryption key rotated out of the key set or because the data was encrypted by a different algorithm.")); + } - private Optional decrypt(EncryptedData data, AesKey key) { - try { - return Optional.of(encryptionStrategy.decrypt(data, key)); - } catch (AEADBadTagException e) { // thrown when wrong key is used for decryption - return Optional.empty(); - } catch (IllegalBlockSizeException - | BadPaddingException - | InvalidKeyException - | NoSuchPaddingException - | NoSuchAlgorithmException - | InvalidAlgorithmParameterException e) { - monitor.warning( - String.format( - DataEncryptionExtension.EXTENSION_NAME - + ": Exception decrypting data using key from rotating key set. %s", - e.getMessage())); - throw new EdcException(e); + private Optional decrypt(EncryptedData data, AesKey key) { + try { + return Optional.of(encryptionStrategy.decrypt(data, key)); + } catch (AEADBadTagException e) { // thrown when wrong key is used for decryption + return Optional.empty(); + } catch (IllegalBlockSizeException + | BadPaddingException + | InvalidKeyException + | NoSuchPaddingException + | NoSuchAlgorithmException + | InvalidAlgorithmParameterException e) { + monitor.warning( + String.format( + DataEncryptionExtension.EXTENSION_NAME + + ": Exception decrypting data using key from rotating key set. %s", + e.getMessage())); + throw new EdcException(e); + } } - } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java index 916ab245f..c40e20b08 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java @@ -21,9 +21,6 @@ package org.eclipse.tractusx.edc.data.encryption.encrypter; -import static java.lang.String.format; - -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.security.Vault; @@ -36,45 +33,52 @@ import org.eclipse.tractusx.edc.data.encryption.provider.CachingKeyProvider; import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; -@RequiredArgsConstructor -public class DataEncrypterFactory { +import static java.lang.String.format; - public static final String AES_ALGORITHM = "AES"; - public static final String NONE = "NONE"; +public class DataEncrypterFactory { - private final Vault vault; - private final Monitor monitor; - private final CryptoKeyFactory keyFactory; + public static final String AES_ALGORITHM = "AES"; + public static final String NONE = "NONE"; - public DataEncrypter createNoneEncrypter() { - return new DataEncrypter() { - @Override - public String encrypt(String data) { - return data; - } + private final Vault vault; + private final Monitor monitor; + private final CryptoKeyFactory keyFactory; - @Override - public String decrypt(String data) { - return data; - } - }; - } + public DataEncrypterFactory(Vault vault, Monitor monitor, CryptoKeyFactory keyFactory) { + this.vault = vault; + this.monitor = monitor; + this.keyFactory = keyFactory; + } - public DataEncrypter createAesEncrypter(AesDataEncrypterConfiguration configuration) { - KeyProvider keyProvider = - new AesKeyProvider(vault, configuration.getKeySetAlias(), keyFactory); + public DataEncrypter createNoneEncrypter() { + return new DataEncrypter() { + @Override + public String encrypt(String data) { + return data; + } - if (configuration.isCachingEnabled()) { - keyProvider = new CachingKeyProvider<>(keyProvider, configuration.getCachingDuration()); + @Override + public String decrypt(String data) { + return data; + } + }; } + + public DataEncrypter createAesEncrypter(AesDataEncrypterConfiguration configuration) { + KeyProvider keyProvider = + new AesKeyProvider(vault, configuration.getKeySetAlias(), keyFactory); - final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); - final AesAlgorithm algorithm = new AesAlgorithm(cryptoDataFactory); + if (configuration.isCachingEnabled()) { + keyProvider = new CachingKeyProvider<>(keyProvider, configuration.getCachingDuration()); + } - monitor.debug( - format( - "AES algorithm was initialised with SecureRandom algorithm '%s'", - algorithm.getAlgorithm())); - return new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); - } + final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); + final AesAlgorithm algorithm = new AesAlgorithm(cryptoDataFactory); + + monitor.debug( + format( + "AES algorithm was initialised with SecureRandom algorithm '%s'", + algorithm.getAlgorithm())); + return new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java index f3fa102a4..7a5b0fc15 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.key; -import lombok.Value; import org.bouncycastle.util.encoders.Base64; public class CryptoKeyFactoryImpl implements CryptoKeyFactory { @@ -37,9 +36,24 @@ public AesKey fromBytes(byte[] key) { return new AesKeyImpl(key, Base64.toBase64String(key)); } - @Value + private static class AesKeyImpl implements AesKey { - byte[] bytes; - String base64; + private final byte[] bytes; + private final String base64; + + private AesKeyImpl(byte[] bytes, String base64) { + this.bytes = bytes; + this.base64 = base64; + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public String getBase64() { + return base64; + } } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java index 82b8eccdd..e740a6f43 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java @@ -19,51 +19,56 @@ */ package org.eclipse.tractusx.edc.data.encryption.provider; -import java.util.Arrays; -import java.util.function.Predicate; -import java.util.stream.Stream; -import lombok.RequiredArgsConstructor; import org.bouncycastle.util.encoders.Base64; import org.eclipse.edc.spi.security.Vault; import org.eclipse.tractusx.edc.data.encryption.DataEncryptionExtension; import org.eclipse.tractusx.edc.data.encryption.key.AesKey; import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; -@RequiredArgsConstructor +import java.util.Arrays; +import java.util.function.Predicate; +import java.util.stream.Stream; + public class AesKeyProvider implements KeyProvider { - private static final String KEY_SEPARATOR = ","; + private static final String KEY_SEPARATOR = ","; + + private final Vault vault; + private final String vaultKeyAlias; + private final CryptoKeyFactory cryptoKeyFactory; - private final Vault vault; - private final String vaultKeyAlias; - private final CryptoKeyFactory cryptoKeyFactory; + public AesKeyProvider(Vault vault, String vaultKeyAlias, CryptoKeyFactory cryptoKeyFactory) { + this.vault = vault; + this.vaultKeyAlias = vaultKeyAlias; + this.cryptoKeyFactory = cryptoKeyFactory; + } - @Override - public Stream getDecryptionKeySet() { - return getKeysStream(); - } + @Override + public AesKey getEncryptionKey() { + return getKeysStream() + .findFirst() + .orElseThrow( + () -> + new RuntimeException( + DataEncryptionExtension.EXTENSION_NAME + + ": Vault must contain at least one key.")); + } - @Override - public AesKey getEncryptionKey() { - return getKeysStream() - .findFirst() - .orElseThrow( - () -> - new RuntimeException( - DataEncryptionExtension.EXTENSION_NAME - + ": Vault must contain at least one key.")); - } + @Override + public Stream getDecryptionKeySet() { + return getKeysStream(); + } - private Stream getKeysStream() { - return Arrays.stream(getKeys().split(KEY_SEPARATOR)) - .map(String::trim) - .filter(Predicate.not(String::isEmpty)) - .map(Base64::decode) - .map(cryptoKeyFactory::fromBytes); - } + private Stream getKeysStream() { + return Arrays.stream(getKeys().split(KEY_SEPARATOR)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .map(Base64::decode) + .map(cryptoKeyFactory::fromBytes); + } - private String getKeys() { - String keys = vault.resolveSecret(vaultKeyAlias); - return keys == null ? "" : keys; - } + private String getKeys() { + String keys = vault.resolveSecret(vaultKeyAlias); + return keys == null ? "" : keys; + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java index b4b490918..4819b6386 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java @@ -20,60 +20,73 @@ package org.eclipse.tractusx.edc.data.encryption.provider; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; + import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.NonNull; -import lombok.Value; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; public class CachingKeyProvider implements KeyProvider { - @NonNull private final KeyProvider decoratedProvider; - @NonNull private final Clock clock; - @NonNull private final Duration cacheExpiration; - - private CachedKeys cachedKeys; - - public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration) { - this(keyProvider, cacheExpiration, Clock.systemUTC()); - } - - public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration, Clock clock) { - - this.decoratedProvider = keyProvider; - this.cacheExpiration = cacheExpiration; - this.clock = clock; - } - - @Override - public T getEncryptionKey() { - checkCache(); - return cachedKeys.getEncryptionKey(); - } - - @Override - public Stream getDecryptionKeySet() { - checkCache(); - return cachedKeys.getDecryptionKeys().stream(); - } - - private void checkCache() { - if (cachedKeys == null || cachedKeys.expiration.isBefore(clock.instant())) { - T encryptionKey = decoratedProvider.getEncryptionKey(); - List decryptionKeys = decoratedProvider.getDecryptionKeySet().collect(Collectors.toList()); - cachedKeys = - new CachedKeys<>(encryptionKey, decryptionKeys, clock.instant().plus(cacheExpiration)); + private final KeyProvider decoratedProvider; + private final Clock clock; + private final Duration cacheExpiration; + + private CachedKeys cachedKeys; + + public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration) { + this(keyProvider, cacheExpiration, Clock.systemUTC()); + } + + public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration, Clock clock) { + this.decoratedProvider = Objects.requireNonNull(keyProvider); + this.cacheExpiration = Objects.requireNonNull(cacheExpiration); + this.clock = Objects.requireNonNull(clock); + } + + @Override + public T getEncryptionKey() { + checkCache(); + return cachedKeys.getEncryptionKey(); + } + + @Override + public Stream getDecryptionKeySet() { + checkCache(); + return cachedKeys.getDecryptionKeys().stream(); + } + + private void checkCache() { + if (cachedKeys == null || cachedKeys.expiration.isBefore(clock.instant())) { + T encryptionKey = decoratedProvider.getEncryptionKey(); + List decryptionKeys = decoratedProvider.getDecryptionKeySet().collect(Collectors.toList()); + cachedKeys = + new CachedKeys<>(encryptionKey, decryptionKeys, clock.instant().plus(cacheExpiration)); + } + } + + + private static class CachedKeys { + private final T encryptionKey; + private final List decryptionKeys; + private final Instant expiration; + + private CachedKeys(T encryptionKey, List decryptionKeys, Instant expiration) { + this.encryptionKey = encryptionKey; + this.decryptionKeys = decryptionKeys; + this.expiration = Objects.requireNonNull(expiration); + } + + public List getDecryptionKeys() { + return decryptionKeys; + } + + public T getEncryptionKey() { + return encryptionKey; + } } - } - - @Value - private static class CachedKeys { - T encryptionKey; - List decryptionKeys; - @NonNull Instant expiration; - } } diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java index 4d19927fb..683a06f08 100644 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; -import lombok.SneakyThrows; import org.bouncycastle.util.encoders.Base64; import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactoryImpl; @@ -31,62 +30,71 @@ class AesAlgorithmTest { - private static final byte[] KEY_128_BIT = Base64.decode("dVUjmYJzbwVcntkFZU+lNQ=="); - private static final byte[] KEY_196_BIT = Base64.decode("NcgHzzRTUC+z396tWG9hqIbeihujz0m8"); - private static final byte[] KEY_256_BIT = - Base64.decode("OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="); - - private final AesAlgorithm strategy = new AesAlgorithm(new CryptoDataFactoryImpl()); - private final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); - - @Test - void test128BitKey() { - testKey(KEY_128_BIT); - } - - @Test - void test196BitKey() { - testKey(KEY_196_BIT); - } - - @Test - void test256BitKey() { - testKey(KEY_256_BIT); - } - - @Test - @SneakyThrows - void testSameDataEncryptedDifferently() { - final AesKey aesKey = createKey(KEY_128_BIT); - final DecryptedData expected = cryptoDataFactory.decryptedFromText("same data"); - final EncryptedData result1 = strategy.encrypt(expected, aesKey); - final EncryptedData result2 = strategy.encrypt(expected, aesKey); - - Assertions.assertNotEquals(result1.getBase64(), result2.getBase64()); - } - - @SneakyThrows - void testKey(byte[] key) { - final AesKey aesKey = createKey(key); - final DecryptedData expected = cryptoDataFactory.decryptedFromText("I will be encrypted"); - final EncryptedData encryptedResult = strategy.encrypt(expected, aesKey); - final DecryptedData result = strategy.decrypt(encryptedResult, aesKey); - - Assertions.assertEquals(expected.getBase64(), result.getBase64()); - } - - AesKey createKey(byte[] key) { - return new AesKey() { - - @Override - public byte[] getBytes() { - return key; - } - - @Override - public String getBase64() { - return Base64.toBase64String(key); - } - }; - } + private static final byte[] KEY_128_BIT = Base64.decode("dVUjmYJzbwVcntkFZU+lNQ=="); + private static final byte[] KEY_196_BIT = Base64.decode("NcgHzzRTUC+z396tWG9hqIbeihujz0m8"); + private static final byte[] KEY_256_BIT = + Base64.decode("OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="); + + private final AesAlgorithm strategy = new AesAlgorithm(new CryptoDataFactoryImpl()); + private final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); + + @Test + void test128BitKey() { + testKey(KEY_128_BIT); + } + + @Test + void test196BitKey() { + testKey(KEY_196_BIT); + } + + @Test + void test256BitKey() { + testKey(KEY_256_BIT); + } + + @Test + void testSameDataEncryptedDifferently() { + final AesKey aesKey = createKey(KEY_128_BIT); + final DecryptedData expected = cryptoDataFactory.decryptedFromText("same data"); + + try { + final EncryptedData result1 = strategy.encrypt(expected, aesKey); + final EncryptedData result2 = strategy.encrypt(expected, aesKey); + + Assertions.assertNotEquals(result1.getBase64(), result2.getBase64()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + void testKey(byte[] key) { + final AesKey aesKey = createKey(key); + final DecryptedData expected = cryptoDataFactory.decryptedFromText("I will be encrypted"); + try { + final EncryptedData encryptedResult = strategy.encrypt(expected, aesKey); + final DecryptedData result = strategy.decrypt(encryptedResult, aesKey); + + + Assertions.assertEquals(expected.getBase64(), result.getBase64()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + AesKey createKey(byte[] key) { + return new AesKey() { + + @Override + public byte[] getBytes() { + return key; + } + + @Override + public String getBase64() { + return Base64.toBase64String(key); + } + }; + } } diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java index ceebf50d6..f70a3bf70 100644 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java @@ -20,61 +20,57 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; -import lombok.SneakyThrows; import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; + class AesInitializationVectorIteratorTest { - @Test - @SneakyThrows - void testDistinctVectors() { - final int vectorCount = 100; - final SecureRandom secureRandom = new SecureRandom(); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(secureRandom); + @Test + void testDistinctVectors() { + final int vectorCount = 100; + final SecureRandom secureRandom = new SecureRandom(); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(secureRandom); - List vectors = new ArrayList<>(); - for (var i = 0; i < vectorCount; i++) { - vectors.add(iterator.next()); - } + List vectors = new ArrayList<>(); + for (var i = 0; i < vectorCount; i++) { + vectors.add(iterator.next()); + } - long distinctVectors = vectors.stream().map(ArrayUtil::byteArrayToHex).distinct().count(); - Assertions.assertEquals(vectorCount, distinctVectors); - } + long distinctVectors = vectors.stream().map(ArrayUtil::byteArrayToHex).distinct().count(); + Assertions.assertEquals(vectorCount, distinctVectors); + } - @Test - @SneakyThrows - void testHasNextTrueOnCounterContinuing() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + @Test + void testHasNextTrueOnCounterContinuing() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - Mockito.when(counter.isMaxed()).thenReturn(false); - Assertions.assertTrue(iterator.hasNext()); - } + Mockito.when(counter.isMaxed()).thenReturn(false); + Assertions.assertTrue(iterator.hasNext()); + } - @Test - @SneakyThrows - void testHasNextFalseOnCounterEnd() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + @Test + void testHasNextFalseOnCounterEnd() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - Mockito.when(counter.isMaxed()).thenReturn(true); - Assertions.assertFalse(iterator.hasNext()); - } + Mockito.when(counter.isMaxed()).thenReturn(true); + Assertions.assertFalse(iterator.hasNext()); + } - @Test - @SneakyThrows - void testNoSuchElementExceptionOnCounterEnd() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + @Test + void testNoSuchElementExceptionOnCounterEnd() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - Mockito.when(counter.isMaxed()).thenReturn(true); - Assertions.assertThrows(NoSuchElementException.class, iterator::next); - } + Mockito.when(counter.isMaxed()).thenReturn(true); + Assertions.assertThrows(NoSuchElementException.class, iterator::next); + } } diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java index aa9140629..6dcd103cb 100644 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.encrypter; -import lombok.SneakyThrows; import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.security.Vault; @@ -42,64 +41,68 @@ @SuppressWarnings("FieldCanBeLocal") class DataEncrypterAesComponentTest { - private static final String KEY_128_BIT_BASE_64 = "7h6sh6t6tchCmNnHjK2kFA=="; - private static final String KEY_256_BIT_BASE_64 = "OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="; - - private DataEncrypter dataEncrypter; - private CryptoAlgorithm algorithm; - private KeyProvider keyProvider; - private CryptoKeyFactory cryptoKeyFactory; - private CryptoDataFactory cryptoDataFactory; - - // mocks - private Monitor monitor; - private Vault vault; - - @BeforeEach - void setup() { - monitor = Mockito.mock(Monitor.class); - vault = Mockito.mock(Vault.class); - - cryptoKeyFactory = new CryptoKeyFactoryImpl(); - cryptoDataFactory = new CryptoDataFactoryImpl(); - algorithm = new AesAlgorithm(cryptoDataFactory); - keyProvider = new AesKeyProvider(vault, "foo", cryptoKeyFactory); - - dataEncrypter = - new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); - } - - @Test - @SneakyThrows - void testKeyRotation() { - Mockito.when(vault.resolveSecret(Mockito.anyString())) - .thenReturn( - String.format( - "%s, %s, %s, %s", - KEY_128_BIT_BASE_64, - KEY_128_BIT_BASE_64, - KEY_128_BIT_BASE_64, - KEY_256_BIT_BASE_64)); - - final AesKey key256Bit = cryptoKeyFactory.fromBase64(KEY_256_BIT_BASE_64); - final String expectedResult = "hello"; - final DecryptedData decryptedResult = cryptoDataFactory.decryptedFromText(expectedResult); - final EncryptedData encryptedResult = algorithm.encrypt(decryptedResult, key256Bit); - - var result = dataEncrypter.decrypt(encryptedResult.getBase64()); - - Assertions.assertEquals(expectedResult, result); - } - - @Test - void testEncryption() { - Mockito.when(vault.resolveSecret(Mockito.anyString())).thenReturn(KEY_128_BIT_BASE_64); - - final String expectedResult = "hello world!"; - - var encryptedResult = dataEncrypter.encrypt(expectedResult); - var result = dataEncrypter.decrypt(encryptedResult); - - Assertions.assertEquals(expectedResult, result); - } + private static final String KEY_128_BIT_BASE_64 = "7h6sh6t6tchCmNnHjK2kFA=="; + private static final String KEY_256_BIT_BASE_64 = "OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="; + + private DataEncrypter dataEncrypter; + private CryptoAlgorithm algorithm; + private KeyProvider keyProvider; + private CryptoKeyFactory cryptoKeyFactory; + private CryptoDataFactory cryptoDataFactory; + + // mocks + private Monitor monitor; + private Vault vault; + + @BeforeEach + void setup() { + monitor = Mockito.mock(Monitor.class); + vault = Mockito.mock(Vault.class); + + cryptoKeyFactory = new CryptoKeyFactoryImpl(); + cryptoDataFactory = new CryptoDataFactoryImpl(); + algorithm = new AesAlgorithm(cryptoDataFactory); + keyProvider = new AesKeyProvider(vault, "foo", cryptoKeyFactory); + + dataEncrypter = + new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); + } + + @Test + void testKeyRotation() { + Mockito.when(vault.resolveSecret(Mockito.anyString())) + .thenReturn( + String.format( + "%s, %s, %s, %s", + KEY_128_BIT_BASE_64, + KEY_128_BIT_BASE_64, + KEY_128_BIT_BASE_64, + KEY_256_BIT_BASE_64)); + + final AesKey key256Bit = cryptoKeyFactory.fromBase64(KEY_256_BIT_BASE_64); + final String expectedResult = "hello"; + final DecryptedData decryptedResult = cryptoDataFactory.decryptedFromText(expectedResult); + + try { + final EncryptedData encryptedResult = algorithm.encrypt(decryptedResult, key256Bit); + + var result = dataEncrypter.decrypt(encryptedResult.getBase64()); + + Assertions.assertEquals(expectedResult, result); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + void testEncryption() { + Mockito.when(vault.resolveSecret(Mockito.anyString())).thenReturn(KEY_128_BIT_BASE_64); + + final String expectedResult = "hello world!"; + + var encryptedResult = dataEncrypter.encrypt(expectedResult); + var result = dataEncrypter.decrypt(encryptedResult); + + Assertions.assertEquals(expectedResult, result); + } } From f52f5f148e8a5b4f5c2ca0e238aac41fa55a2ae4 Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Wed, 5 Apr 2023 09:33:01 +0200 Subject: [PATCH 039/132] Update edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- .../edc/data/encryption/algorithms/aes/AesAlgorithmTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java index 683a06f08..d141887cf 100644 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java @@ -75,8 +75,6 @@ void testKey(byte[] key) { try { final EncryptedData encryptedResult = strategy.encrypt(expected, aesKey); final DecryptedData result = strategy.decrypt(encryptedResult, aesKey); - - Assertions.assertEquals(expected.getBase64(), result.getBase64()); } catch (Exception e) { throw new RuntimeException(e); From 6ef8522fb91dfeb3337ed19985e562795d1472f7 Mon Sep 17 00:00:00 2001 From: Tuncay Tunc Date: Wed, 5 Apr 2023 10:22:13 +0200 Subject: [PATCH 040/132] Fix issue with sql pool --- edc-controlplane/edc-controlplane-postgresql/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts index 659aa6891..b69e3d010 100644 --- a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts @@ -12,9 +12,10 @@ dependencies { runtimeOnly(project(":edc-extensions:postgresql-migration")) runtimeOnly(edc.azure.vault) runtimeOnly(edc.bundles.sqlstores) + runtimeOnly(edc.transaction.local) + runtimeOnly(edc.sql.pool) runtimeOnly(edc.core.controlplane) runtimeOnly(edc.dpf.transfer) - } From 985a2f0f74cc3f574054fe47a42341965f402611 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Tue, 11 Apr 2023 09:02:51 +0200 Subject: [PATCH 041/132] fix: add newline to file --- .github/workflows/verify.yaml | 2 -- .../decision-records/2023-04-03_renaming_branches/README.md | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index b51c482f1..c9c8f1d5f 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -34,8 +34,6 @@ on: pull_request: paths-ignore: - 'charts/**' - - 'docs/**' - - '**/*.md' branches: - '*' workflow_dispatch: diff --git a/docs/development/decision-records/2023-04-03_renaming_branches/README.md b/docs/development/decision-records/2023-04-03_renaming_branches/README.md index 7bc3abd64..5638a79dd 100644 --- a/docs/development/decision-records/2023-04-03_renaming_branches/README.md +++ b/docs/development/decision-records/2023-04-03_renaming_branches/README.md @@ -58,4 +58,4 @@ like force-pushing. Write access to `upstream` is required!_ The new `releases` branch (note the plural) will serve the same purpose that `main` did up until now, which is to track all releases (via merge commits and tags) in chronological order. We will continue to have separate `release/x.y.z` -branches for every release. \ No newline at end of file +branches for every release. From 5975acb49de69e07aa003fa41bda6ed2bdd043f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 08:34:44 +0000 Subject: [PATCH 042/132] chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../edc-controlplane-postgresql/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index b3e04fac7..c9af99a81 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 9c6b0284fa6a5fff99c353fc896ea794f86acd1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:19:35 +0000 Subject: [PATCH 043/132] chore(deps): bump actions/setup-java from 3.10.0 to 3.11.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3.10.0 to 3.11.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3.10.0...v3.11.0) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yaml | 8 ++++---- .github/workflows/business-tests.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 4 ++-- .github/workflows/veracode.yaml | 6 +++--- .github/workflows/verify.yaml | 12 ++++++------ 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b8ab83bba..48e463202 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -74,7 +74,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -118,7 +118,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -185,7 +185,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -240,7 +240,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 6a7cd2cbf..9273b781c 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -53,7 +53,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set-Up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index acb4412d9..59969decc 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -34,7 +34,7 @@ jobs: git config user.email noreply@github.com - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 56148b82a..23455015d 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -58,7 +58,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -179,7 +179,7 @@ jobs: prerelease: false - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index 0bfaac8b5..b980a2829 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -31,7 +31,7 @@ jobs: fetch-depth: 0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -60,7 +60,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -109,7 +109,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index c9c8f1d5f..5cdaa5c9c 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -61,7 +61,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -94,7 +94,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -111,7 +111,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -128,7 +128,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -145,7 +145,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' @@ -166,7 +166,7 @@ jobs: with: fetch-depth: 0 - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 + uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' From afc89ba42b9c95c3635e8d95376dd67b90600615 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:22:50 +0000 Subject: [PATCH 044/132] chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index b3e04fac7..c9af99a81 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 0e57ad0b32aa8fe649e490287a76a1b4fa4ea53a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:37:22 +0000 Subject: [PATCH 045/132] chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../edc-controlplane-memory/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index b3e04fac7..c9af99a81 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 3684fbe1b0f09f170338f3900e3bc71c3f42ebbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:37:30 +0000 Subject: [PATCH 046/132] chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../edc-dataplane-azure-vault/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index 5c3b12f11..5c9e65d5e 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 0de7accb3fc237a5c74313d56e86c234aec8033d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:53:03 +0000 Subject: [PATCH 047/132] chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 5c3b12f11..5c9e65d5e 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" From 94e2fb402b03dd40bd66dd810b4a9688aef7bd96 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Tue, 11 Apr 2023 15:36:36 +0200 Subject: [PATCH 048/132] docs: create decision-record about refactoring helm charts --- .../2023-04-11_refactor_helmcharts/README.md | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 docs/development/decision-records/2023-04-11_refactor_helmcharts/README.md diff --git a/docs/development/decision-records/2023-04-11_refactor_helmcharts/README.md b/docs/development/decision-records/2023-04-11_refactor_helmcharts/README.md new file mode 100644 index 000000000..5cf59f958 --- /dev/null +++ b/docs/development/decision-records/2023-04-11_refactor_helmcharts/README.md @@ -0,0 +1,112 @@ +# Refactor TractusX-EDC Helm charts + +## Decision + +The Helm charts provided by Tractusx-EDC will be refactored to be more focused and opinionated. Specifically, there will +be the following charts: + +1. `tractusx-connector-memory`: all backing stores are memory-based and thus ephemeral. The vault will also be + memory-based. _This chart is intended for testing/demo purposes only!_ +2. `tractusx-connector`: this is the "production-ready" chart that uses PostgreSQL and Hashicorp-Vault +3. `tractusx-connector-azure-vault`: this is a variant of `tractusx-connector-azure-vault` that uses Azure KeyVault (" + AZKV") instead + of Hashicorp as some stakeholders still use AZKV. + +These charts and their default configuration will be fully [tested](#testing). + +In addition to that, the Docker images will undergo some [refactoring](#docker-image-refactoring) as well. + +## Rationale + +The current "dynamically composed" helm chart has proven to be a source for issues, and it is difficult to isolate +errors due to the great number of variations. Further, only one particular variant (i.e. postgres+hashicorp) is put to +any semblance of testing (i.e. business tests). + +The official recommendation of TractusX-EDC is to use PostgreSQL and HashiCorp Vault, and alongside it, we will provide +charts for easy testing and setting up demos as well as an Azure KeyVault variant for legacy use cases. + +> Note: using Azure KeyVault is not officially supported or recommended by TractusX-EDC! + +This will also reduce the number of Docker images that need to be published. + +## Approach + +### Variant 1: `tractusx-connector-memory` + +This chart is intended for blackbox-testing or for easily setting up demos etc. It is **not** recommended for anything +else. It will have the following properties: + +- all backing stores (Asset Index, Policy Store etc.) are ephemeral in-memory stores +- the vault implementation will either be based also on memory, or on the `FsVault`, which uses local storage to store + secrets +- an embedded data plane will be used +- no scalability or replication is possible +- DAPS will be used as identity provider, so there is an implicit dependency onto a DAPS instance +- the `edc-runtime-memory` Docker image will be used. That image contains both control plane and data plane. + +### Variant 2: `tractusx-connector` + +This is the production-ready chart that is published by TractusX-EDC, and it will actually consist of two charts. One is +the `tractusx-runtime` sub-chart, that contains all configuration for data plane and control plane, and the other one is +the top-level `tractusx-connector` chart, that pulls in other charts as dependencies that are needed for one TractusX +connector application. This is sometimes referred to +as ["umbrella chart"](https://helm.sh/docs/howto/charts_tips_and_tricks/#complex-charts-with-many-dependencies). + +> Note: this will **not** include sub-charts for DAPS or MinIO. + +```shell +tractusx-connector + |-> tractusx-runtime + |-> postgres + |-> hashicorp-vault +``` + +The `tractusx-runtime` chart has the following properties: + +- PostgreSQL is used as persistence backend +- HashiCorp Vault is used as secret store +- the data plane is a separate runtime, i.e. separate pod +- DAPS is used as identity provider +- the `edc-controlplane-postgresql-hashicorp-vault` and `edc-dataplane-hashicorp-vault` Docker images will be used + +### Variant 3: `tractusx-connector-azure-vault` + +This variant is essentially identical to `tractusx-connector` except for dropping the HashiCorp Vault chart, and +replacing the HashiCorp Vault configuration with Azure KeyVault configuration. + +For this, the `edc-controlplane-postgresql-azure-vault` and `edc-dataplane-azure-vault` Docker images will be used. + +### Testing + +There are several steps to testing our Helm charts: + +1. waiting for all pods to come up: using an exemplary configuration, this relies on the health checks, i.e. liveness + and readiness probe (i.e. the runtime`s observability endpoints) to ensure that (most of) the static + configuration is correct, no values are missing etc. +2. executing a set of HTTP requests against the management API and assert a successful HTTP status code. For that we + use [Helm chart tests](https://helm.sh/docs/topics/chart_tests/) + +> Note: we refer to this kind of testing as "deployment testing" + +### Docker image refactoring + +The following changes need to be made to our Docker images: + +- rename `edc-controlplane-memory` -> `-edc-runtime-memory` +- in `edc-runtime-memory` use `FsVault` instead of `AzureVault` +- `edc-runtime-memory` contains an embedded data plane +- rename `edc-controlplane-postgresql` -> `edc-controlplane-postgresql-azure-vault` +- delete `edc-controlplane-memory-hashicorp-vault` + +thus effectively resulting in the following structure: + +```shell +edc-controlplane +|-> edc-runtime-memory +|-> edc-controlplane-postgresql-hashicorp-vault +|-> edc-controlplane-postgresql-azure-vault + +edc-dataplane +|-> edc-dataplane-hashicorp-vault +|-> edc-dataplane-azure-vaul +``` From 302ce5dae04947d52c3c0e706a21b2ff665c74e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 02:02:36 +0000 Subject: [PATCH 049/132] chore(deps): bump crazy-max/ghaction-import-gpg from 1 to 5 Bumps [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) from 1 to 5. - [Release notes](https://github.com/crazy-max/ghaction-import-gpg/releases) - [Changelog](https://github.com/crazy-max/ghaction-import-gpg/blob/v5/CHANGELOG.md) - [Commits](https://github.com/crazy-max/ghaction-import-gpg/compare/v1...v5) --- updated-dependencies: - dependency-name: crazy-max/ghaction-import-gpg dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 48e463202..093c7f6b4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -246,7 +246,7 @@ jobs: distribution: 'temurin' cache: 'gradle' - name: Import GPG Key - uses: crazy-max/ghaction-import-gpg@v1 + uses: crazy-max/ghaction-import-gpg@v5 env: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 23455015d..e3d4d555f 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -65,7 +65,7 @@ jobs: cache: 'gradle' - name: Import GPG Key - uses: crazy-max/ghaction-import-gpg@v1 + uses: crazy-max/ghaction-import-gpg@v5 env: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} From 4ad4322b36938ea20291f13dfd84bf7a17e788ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 02:02:38 +0000 Subject: [PATCH 050/132] chore(deps): bump helm/chart-testing-action from 2.3.1 to 2.4.0 Bumps [helm/chart-testing-action](https://github.com/helm/chart-testing-action) from 2.3.1 to 2.4.0. - [Release notes](https://github.com/helm/chart-testing-action/releases) - [Commits](https://github.com/helm/chart-testing-action/compare/v2.3.1...v2.4.0) --- updated-dependencies: - dependency-name: helm/chart-testing-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/helm-lint.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 624607533..bf1531cad 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -42,7 +42,7 @@ jobs: python-version: 3.7 - name: chart-testing (setup) - uses: helm/chart-testing-action@v2.3.1 + uses: helm/chart-testing-action@v2.4.0 ##################### ### Chart Testing ### ##################### From 2d7652da1d9b9aa2db445b40c951c9ddf8dc0b04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 02:02:45 +0000 Subject: [PATCH 051/132] chore(deps): bump mikefarah/yq from 4.31.2 to 4.33.3 Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.31.2 to 4.33.3. - [Release notes](https://github.com/mikefarah/yq/releases) - [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt) - [Commits](https://github.com/mikefarah/yq/compare/v4.31.2...v4.33.3) --- updated-dependencies: - dependency-name: mikefarah/yq dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/draft-new-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 59969decc..915f2b7a8 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -49,7 +49,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Bump version in /charts - uses: mikefarah/yq@v4.31.2 + uses: mikefarah/yq@v4.33.3 with: cmd: |- find charts -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' From 14b6d87139aa9719c56babb066310bbea1c336ca Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 15:14:06 +0200 Subject: [PATCH 052/132] feature: publish docker images to DockerHub --- .github/workflows/publish-docker.yaml | 0 .../notice.md | 28 +++++++++++++++++ .../edc-controlplane-memory/notice.md | 28 +++++++++++++++++ .../notice.md | 31 +++++++++++++++++++ .../edc-controlplane-postgresql/notice.md | 28 +++++++++++++++++ 5 files changed, 115 insertions(+) create mode 100644 .github/workflows/publish-docker.yaml create mode 100644 edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md create mode 100644 edc-controlplane/edc-controlplane-memory/notice.md create mode 100644 edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md create mode 100644 edc-controlplane/edc-controlplane-postgresql/notice.md diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md new file mode 100644 index 000000000..1285fdc3d --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md @@ -0,0 +1,28 @@ +## Notice for Docker image + +This application provides container images for demonstration purposes. + +DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory-hashicorp-vault + +Eclipse Tractus-X product(s) installed within the image: + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- +Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) + +**Used base image** + +- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. + diff --git a/edc-controlplane/edc-controlplane-memory/notice.md b/edc-controlplane/edc-controlplane-memory/notice.md new file mode 100644 index 000000000..639e0d366 --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory/notice.md @@ -0,0 +1,28 @@ +## Notice for Docker image + +This application provides container images for demonstration purposes. + +DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory + +Eclipse Tractus-X product(s) installed within the image: + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- +Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) + +**Used base image** + +- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. + diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md new file mode 100644 index 000000000..5cc869306 --- /dev/null +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md @@ -0,0 +1,31 @@ +## Notice for Docker image + +This application provides container images for demonstration purposes. + +DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql-hashicorp-vault + +Eclipse Tractus-X product(s) installed within the image: + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- + +Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile + +- Project + license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) + +**Used base image** + +- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. + diff --git a/edc-controlplane/edc-controlplane-postgresql/notice.md b/edc-controlplane/edc-controlplane-postgresql/notice.md new file mode 100644 index 000000000..ec36137a1 --- /dev/null +++ b/edc-controlplane/edc-controlplane-postgresql/notice.md @@ -0,0 +1,28 @@ +## Notice for Docker image + +This application provides container images for demonstration purposes. + +DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql + +Eclipse Tractus-X product(s) installed within the image: + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- +Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) + +**Used base image** + +- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. + From cb4156a339dc30c0cc0bfd556d201414bddef26d Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 15:40:27 +0200 Subject: [PATCH 053/132] add manual docker-publish workflow --- .../actions/publish-docker-image/action.yml | 72 ++++++++++++++++++ .github/workflows/publish-docker.yaml | 76 +++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 .github/actions/publish-docker-image/action.yml diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml new file mode 100644 index 000000000..15638b13f --- /dev/null +++ b/.github/actions/publish-docker-image/action.yml @@ -0,0 +1,72 @@ +name: "Publish Docker Image" +description: "Build and publish a Docker Image to DockerHub" +inputs: + rootDir: + required: true + description: "The directory where the notice.md file and the src/main/docker directory are located" + namespace: + required: false + default: "tractusx" + description: "The Docker image namespace" + imagename: + required: true + description: "the name of the image" +runs: + using: "composite" + steps: + - name: Checkout + uses: actions/checkout@v3 + + ##################### + # Login to DockerHub + ##################### + - name: DockerHub login + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + + ############################### + # Set metadata of docker image + ############################### + # Create SemVer or ref tags dependent of trigger event + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ${{ inputs.namespace }}/${{ inputs.imagename }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}} + type=semver,pattern={{major}}.{{minor}} + + ############################### + # Build and push the image + ############################### + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + file: ${{ inputs.rootDir }}/src/main/docker/Dockerfile + build-args: | + JAR=${{ inputs.rootDir }}/build/libs/${{ inputs.imagename }}.jar + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + ############################### + # Update the description + # https://github.com/peter-evans/dockerhub-description + ############################### + - name: Update Docker Hub description + if: github.event_name != 'pull_request' && ${{ secrets.DOCKER_HUB_USER }} && ${{ secrets.DOCKER_HUB_TOKEN }} + uses: peter-evans/dockerhub-description@v3 + with: + readme-filepath: ${{ inputs.rootDir }}/notice.md + username: ${{ secrets.DOCKER_HUB_USER }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + repository: ${{ inputs.namespace }}/${{ inputs.imagename }} \ No newline at end of file diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index e69de29bb..240895fed 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -0,0 +1,76 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +name: "Create Docker images" + +on: + workflow_dispatch: + inputs: + rootDir: + required: true + description: "The directory where the notice.md file and the src/main/docker directory are located" + namespace: + required: false + default: "tractusx" + description: "The Docker image namespace" + imagename: + required: true + description: "the name of the image" + +jobs: + create-docker-image-controlplane: + name: "Create Docker Images for the ControlPlane" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: + - edc-controlplane-memory + - edc-controlplane-memory-hashicorp-vault + - edc-controlplane-postgresql + - edc-controlplane-postgresql-hashicorp-vault + permissions: + contents: write + packages: write + steps: + - uses: ./.github/actions/publish-docker-image + with: + rootDir: edc-controlplane/${{ matrix.name }} + imagename: ${{ matrix.name }} + + + create-docker-image-dataplane: + name: "Create Docker Images for the DataPlane" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: + - edc-dataplane-azure-vault + - edc-dataplane-hashicorp-vault + permissions: + contents: write + packages: write + steps: + - uses: ./.github/actions/publish-docker-image + with: + rootDir: edc-dataplane/${{ matrix.name }} + imagename: ${{ matrix.name }} \ No newline at end of file From 189b43458193035e22d3ce8a9016aa6132fbcf20 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 15:47:52 +0200 Subject: [PATCH 054/132] avoid input params, add concurrency --- .github/workflows/publish-docker.yaml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index 240895fed..ffde68a0b 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -23,17 +23,10 @@ name: "Create Docker images" on: workflow_dispatch: - inputs: - rootDir: - required: true - description: "The directory where the notice.md file and the src/main/docker directory are located" - namespace: - required: false - default: "tractusx" - description: "The Docker image namespace" - imagename: - required: true - description: "the name of the image" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: create-docker-image-controlplane: From e58a3a0cd62bee276ad3781e2fff543700caa376 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 15:49:57 +0200 Subject: [PATCH 055/132] add checkout action --- .github/workflows/publish-docker.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index ffde68a0b..7b868b053 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -44,6 +44,8 @@ jobs: contents: write packages: write steps: + - name: Checkout + uses: actions/checkout@v3 - uses: ./.github/actions/publish-docker-image with: rootDir: edc-controlplane/${{ matrix.name }} @@ -63,6 +65,8 @@ jobs: contents: write packages: write steps: + - name: Checkout + uses: actions/checkout@v3 - uses: ./.github/actions/publish-docker-image with: rootDir: edc-dataplane/${{ matrix.name }} From f8d6ee2b9fa4b74d0d823d3df92cdeb6bbcb3ffc Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 15:54:42 +0200 Subject: [PATCH 056/132] creds as action inputs --- .github/actions/publish-docker-image/action.yml | 16 +++++++++++----- .github/workflows/publish-docker.yaml | 6 +++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 15638b13f..c8cc42629 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -11,6 +11,12 @@ inputs: imagename: required: true description: "the name of the image" + docker_user: + required: false + description: "DockerHub user name. No push is done if omitted" + docker_token: + required: false + description: "DockerHub Token. No push is done if omitted" runs: using: "composite" steps: @@ -24,8 +30,8 @@ runs: if: github.event_name != 'pull_request' uses: docker/login-action@v2 with: - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_TOKEN }} + username: ${{ inputs.docker_user }} + password: ${{ inputs.docker_token }} ############################### # Set metadata of docker image @@ -63,10 +69,10 @@ runs: # https://github.com/peter-evans/dockerhub-description ############################### - name: Update Docker Hub description - if: github.event_name != 'pull_request' && ${{ secrets.DOCKER_HUB_USER }} && ${{ secrets.DOCKER_HUB_TOKEN }} + if: github.event_name != 'pull_request' && ${{ inputs.docker_user }} && ${{ inputs.docker_token }} uses: peter-evans/dockerhub-description@v3 with: readme-filepath: ${{ inputs.rootDir }}/notice.md - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_TOKEN }} + username: ${{ inputs.docker_user }} + password: ${{ inputs.docker_token }} repository: ${{ inputs.namespace }}/${{ inputs.imagename }} \ No newline at end of file diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index 7b868b053..8c25a3fab 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -50,6 +50,8 @@ jobs: with: rootDir: edc-controlplane/${{ matrix.name }} imagename: ${{ matrix.name }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} create-docker-image-dataplane: @@ -70,4 +72,6 @@ jobs: - uses: ./.github/actions/publish-docker-image with: rootDir: edc-dataplane/${{ matrix.name }} - imagename: ${{ matrix.name }} \ No newline at end of file + imagename: ${{ matrix.name }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} \ No newline at end of file From 6e93812cbb1c222f441e4b484d254ec68a28baa1 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 16:02:59 +0200 Subject: [PATCH 057/132] add jar build step --- .github/actions/publish-docker-image/action.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index c8cc42629..595fdda35 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -33,6 +33,20 @@ runs: username: ${{ inputs.docker_user }} password: ${{ inputs.docker_token }} + ##################### + # Build JAR file + ##################### + - name: Set up JDK 11 + uses: actions/setup-java@v3.11.0 + with: + java-version: '11' + distribution: 'temurin' + cache: 'gradle' + - name: Build Controlplane + shell: bash + run: |- + ./gradlew -p ${{ inputs.rootDir }} shadowJar + ############################### # Set metadata of docker image ############################### From e56a86b21b2a9cbaf98a9fcb82b5f3923f667db2 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 16:09:46 +0200 Subject: [PATCH 058/132] make namespace overridable --- .github/workflows/publish-docker.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index 8c25a3fab..4bb7a4045 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -23,6 +23,11 @@ name: "Create Docker images" on: workflow_dispatch: + inputs: + namespace: + description: 'The namespace (=repo) in DockerHub' + required: false + default: "tractusx" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -50,6 +55,7 @@ jobs: with: rootDir: edc-controlplane/${{ matrix.name }} imagename: ${{ matrix.name }} + namespace: ${{ inputs.namespace }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} @@ -73,5 +79,6 @@ jobs: with: rootDir: edc-dataplane/${{ matrix.name }} imagename: ${{ matrix.name }} + namespace: ${{ inputs.namespace }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} \ No newline at end of file From 062ddb75e287ff784017df381cbe3124ba496fb6 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 16:30:07 +0200 Subject: [PATCH 059/132] updated notices --- .../notice.md | 2 +- .../notice.md | 2 +- .../edc-controlplane-postgresql/notice.md | 2 +- .../edc-dataplane-azure-vault/notice.md | 27 ++++++++++++++++++ .../edc-dataplane-hashicorp-vault/notice.md | 28 +++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 edc-dataplane/edc-dataplane-azure-vault/notice.md create mode 100644 edc-dataplane/edc-dataplane-hashicorp-vault/notice.md diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md index 1285fdc3d..2508b9a8e 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md @@ -1,6 +1,6 @@ ## Notice for Docker image -This application provides container images for demonstration purposes. +An EDC Control Plane using memory-based storage, and HashiCorp Vault as secret store. DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory-hashicorp-vault diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md index 5cc869306..169a58361 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md @@ -1,6 +1,6 @@ ## Notice for Docker image -This application provides container images for demonstration purposes. +An EDC Control Plane using PostgreSQL as persistence backend, and HashiCorp Vault as secret store. DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql-hashicorp-vault diff --git a/edc-controlplane/edc-controlplane-postgresql/notice.md b/edc-controlplane/edc-controlplane-postgresql/notice.md index ec36137a1..5aafea71a 100644 --- a/edc-controlplane/edc-controlplane-postgresql/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql/notice.md @@ -1,6 +1,6 @@ ## Notice for Docker image -This application provides container images for demonstration purposes. +An EDC Control Plane using PostgreSQL as persistence backend, and Azure KeyVault as secret store. DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql diff --git a/edc-dataplane/edc-dataplane-azure-vault/notice.md b/edc-dataplane/edc-dataplane-azure-vault/notice.md new file mode 100644 index 000000000..ec2afd457 --- /dev/null +++ b/edc-dataplane/edc-dataplane-azure-vault/notice.md @@ -0,0 +1,27 @@ +## Notice for Docker image + +An EDC Data Plane using the Azure KeyVault. + +DockerHub: https://hub.docker.com/r/tractusx/edc-dataplane-azure-vault + +Eclipse Tractus-X product(s) installed within the image: + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) + +**Used base image** + +- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. + diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md new file mode 100644 index 000000000..afcd9c8a6 --- /dev/null +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md @@ -0,0 +1,28 @@ +## Notice for Docker image + +An EDC Data Plane using the HashiCorp Vault + +DockerHub: https://hub.docker.com/r/tractusx/edc-dataplane-hashicorp-vault + +Eclipse Tractus-X product(s) installed within the image: + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- +Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) + +**Used base image** + +- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. + From 03dd9dbcee9239a46c2372052a54fd3535e335ba Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 16:54:30 +0200 Subject: [PATCH 060/132] incorporate new docker publish flow --- .github/workflows/build.yaml | 153 +++++++---------------------------- 1 file changed, 31 insertions(+), 122 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 48e463202..c52b671f5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -55,8 +55,7 @@ jobs: GPG_PRIVATE_KEY: ${{ steps.secret-presence.outputs.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ steps.secret-presence.outputs.GPG_PASSPHRASE }} steps: - - - name: Check whether secrets exist + - name: Check whether secrets exist id: secret-presence run: | [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "::set-output name=SONAR_TOKEN::true" @@ -66,22 +65,19 @@ jobs: build-extensions: runs-on: ubuntu-latest - needs: [ secret-presence] + needs: [ secret-presence ] steps: # Set-Up - - - name: Checkout + - name: Checkout uses: actions/checkout@v3.3.0 - - - name: Set up JDK 11 + - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: java-version: '11' distribution: 'temurin' cache: 'gradle' # Build - - - name: Build Extensions + - name: Build Extensions run: |- ./gradlew -p edc-extensions build env: @@ -89,11 +85,9 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} build-controlplane: + name: "Create Docker Images for the ControlPlane" runs-on: ubuntu-latest - permissions: - contents: read - packages: write - needs: [ secret-presence] + needs: [ secrets-presence ] strategy: fail-fast: false matrix: @@ -102,134 +96,49 @@ jobs: - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault + permissions: + contents: write + packages: write steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3.3.0 - - - name: Login to GitHub Container Registry - if: | - github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' - # Build - - - name: Build Controlplane - run: |- - ./gradlew -p edc-controlplane/${{ matrix.name }} shadowJar - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: edc-controlplane Docker Metadata - id: edc_controlplane_meta - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/${{ matrix.name }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build Docker Image - uses: docker/build-push-action@v4 + - name: Checkout + uses: actions/checkout@v3 + - uses: ./.github/actions/publish-docker-image with: - context: . - file: edc-controlplane/${{ matrix.name }}/src/main/docker/Dockerfile - build-args: | - JAR=edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - push: | - ${{ (github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_controlplane_meta.outputs.tags }} - labels: ${{ steps.edc_controlplane_meta.outputs.labels }} + rootDir: edc-controlplane/${{ matrix.name }} + imagename: ${{ matrix.name }} + namespace: ${{ inputs.namespace }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} build-dataplane: runs-on: ubuntu-latest - permissions: - contents: read - packages: write - needs: [ secret-presence] + needs: [ secret-presence ] strategy: fail-fast: false matrix: name: - edc-dataplane-azure-vault - edc-dataplane-hashicorp-vault + permissions: + contents: write + packages: write steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3.3.0 - - - name: Login to GitHub Container Registry - if: | - github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' - # Build - - - name: Build Dataplane - run: |- - ./gradlew -p edc-dataplane/${{ matrix.name }} shadowJar - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: edc-dataplane Docker Metadata - id: edc_dataplane_meta - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/${{ matrix.name }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build Docker Image - uses: docker/build-push-action@v4 + - name: Checkout + uses: actions/checkout@v3 + - uses: ./.github/actions/publish-docker-image with: - context: . - file: edc-dataplane/${{ matrix.name }}/src/main/docker/Dockerfile - build-args: | - JAR=edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - push: | - ${{ (github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_dataplane_meta.outputs.tags }} - labels: ${{ steps.edc_dataplane_meta.outputs.labels }} + rootDir: edc-dataplane/${{ matrix.name }} + imagename: ${{ matrix.name }} + namespace: ${{ inputs.namespace }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} publish-to-github-packages: runs-on: ubuntu-latest permissions: contents: read packages: write - needs: [secret-presence, build-controlplane, build-dataplane, build-extensions] + needs: [ secret-presence, build-controlplane, build-dataplane, build-extensions ] # do not run on PR branches, do not run on main if: | From 69171f8fde3dc925fa8ea22afe777b9700bd1bed Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 16:56:12 +0200 Subject: [PATCH 061/132] update chart deployment specs --- .../actions/publish-docker-image/action.yml | 21 ++++++++++++ .../templates/deployment-controlplane.yaml | 8 ++--- .../templates/deployment-dataplane.yaml | 34 +++++++++++++++---- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 595fdda35..7cbe07297 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -1,3 +1,24 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- name: "Publish Docker Image" description: "Build and publish a Docker Image to DockerHub" inputs: diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 691047c0f..dc708a8a7 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -62,13 +62,13 @@ spec: {{- if .Values.controlplane.image.repository }} image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.postgresql.enabled .Values.vault.hashicorp.enabled }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.postgresql.enabled .Values.vault.azure.enabled }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-postgresql:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-controlplane-postgresql:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.hashicorp.enabled }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.azure.enabled }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-controlplane-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else }} {{- fail "cannot choose control-plane image automatically based on configuration" }} {{- end }} diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index ff5f6a5ce..bd375b295 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + --- apiVersion: apps/v1 kind: Deployment @@ -40,9 +62,9 @@ spec: {{- if .Values.dataplane.image.repository }} image: "{{ .Values.dataplane.image.repository }}:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.vault.hashicorp }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.azure }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else }} {{- fail "cannot choose data-plane image automatically based on configuration" }} {{- end }} @@ -109,7 +131,7 @@ spec: - name: "WEB_HTTP_PUBLIC_PATH" value: {{ .Values.dataplane.endpoints.public.path | quote }} - name: "EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT" - value: {{ include "txdc.controlplane.url.validation" .}} + value: {{ include "txdc.controlplane.url.validation" .}} ####### # AWS # @@ -162,9 +184,9 @@ spec: value: {{ .Values.vault.azure.certificate | quote }} {{- end }} - ###################################### - ## Additional environment variables ## - ###################################### + ###################################### + ## Additional environment variables ## + ###################################### {{- range $key, $value := .Values.dataplane.envValueFrom }} - name: {{ $key | quote }} valueFrom: From 2ba15ea32b3c48e37f9d98d14e44c9a5538975a6 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 17:19:56 +0200 Subject: [PATCH 062/132] fix formatting --- .github/workflows/build.yaml | 2 -- .../edc-controlplane-memory-hashicorp-vault/notice.md | 5 +++-- edc-controlplane/edc-controlplane-memory/notice.md | 7 ++++--- .../notice.md | 10 ++++------ edc-controlplane/edc-controlplane-postgresql/notice.md | 5 +++-- edc-dataplane/edc-dataplane-azure-vault/notice.md | 2 ++ edc-dataplane/edc-dataplane-hashicorp-vault/notice.md | 5 +++-- 7 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c52b671f5..660600a73 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -98,7 +98,6 @@ jobs: - edc-controlplane-postgresql-hashicorp-vault permissions: contents: write - packages: write steps: - name: Checkout uses: actions/checkout@v3 @@ -121,7 +120,6 @@ jobs: - edc-dataplane-hashicorp-vault permissions: contents: write - packages: write steps: - name: Checkout uses: actions/checkout@v3 diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md index 2508b9a8e..d1db501b3 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md @@ -6,10 +6,11 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory-hashicorp-v Eclipse Tractus-X product(s) installed within the image: +### TractusX-EDC Control Plane + - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- -Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) **Used base image** diff --git a/edc-controlplane/edc-controlplane-memory/notice.md b/edc-controlplane/edc-controlplane-memory/notice.md index 639e0d366..5806989b4 100644 --- a/edc-controlplane/edc-controlplane-memory/notice.md +++ b/edc-controlplane/edc-controlplane-memory/notice.md @@ -1,15 +1,16 @@ ## Notice for Docker image -This application provides container images for demonstration purposes. +An EDC Control Plane using memory-based storage, and Azure KeyVault as secret store. DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory Eclipse Tractus-X product(s) installed within the image: +### TractusX-EDC Control Plane + - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- -Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) **Used base image** diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md index 169a58361..ac8288747 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md @@ -6,14 +6,12 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql-hashico Eclipse Tractus-X product(s) installed within the image: +### TractusX-EDC Control Plane + - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- - -Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile - -- Project - license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) **Used base image** diff --git a/edc-controlplane/edc-controlplane-postgresql/notice.md b/edc-controlplane/edc-controlplane-postgresql/notice.md index 5aafea71a..edb15438d 100644 --- a/edc-controlplane/edc-controlplane-postgresql/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql/notice.md @@ -6,10 +6,11 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql Eclipse Tractus-X product(s) installed within the image: +### TractusX-EDC Control Plane + - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- -Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) **Used base image** diff --git a/edc-dataplane/edc-dataplane-azure-vault/notice.md b/edc-dataplane/edc-dataplane-azure-vault/notice.md index ec2afd457..a161899ef 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/notice.md +++ b/edc-dataplane/edc-dataplane-azure-vault/notice.md @@ -6,6 +6,8 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-dataplane-azure-vault Eclipse Tractus-X product(s) installed within the image: +### TractusX-EDC Data Plane + - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx - Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md index afcd9c8a6..90c281a2f 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md @@ -6,10 +6,11 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-dataplane-hashicorp-vault Eclipse Tractus-X product(s) installed within the image: +### TractusX-EDC Data Plane + - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- -Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) **Used base image** From 26fb63f35e48674df2afd5887b51680734ba970e Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 17:27:44 +0200 Subject: [PATCH 063/132] markdown lint --- .markdownlint.yaml | 1 + .../edc-controlplane-memory-hashicorp-vault/notice.md | 7 +++---- edc-controlplane/edc-controlplane-memory/notice.md | 7 +++---- .../edc-controlplane-postgresql-hashicorp-vault/notice.md | 7 +++---- edc-controlplane/edc-controlplane-postgresql/notice.md | 7 +++---- edc-dataplane/edc-dataplane-azure-vault/notice.md | 7 +++---- edc-dataplane/edc-dataplane-hashicorp-vault/notice.md | 7 +++---- 7 files changed, 19 insertions(+), 24 deletions(-) diff --git a/.markdownlint.yaml b/.markdownlint.yaml index ace38e3d4..d060f2264 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -19,6 +19,7 @@ "default": true # Do not restrict line length: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#MD013 "MD013": false +"MD034": # Allow same content on headlines on siblings: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#MD024 "MD024": "siblings_only": true diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md index d1db501b3..cf6aa8d92 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md @@ -1,4 +1,4 @@ -## Notice for Docker image +# Notice for Docker image An EDC Control Plane using memory-based storage, and HashiCorp Vault as secret store. @@ -6,14 +6,14 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory-hashicorp-v Eclipse Tractus-X product(s) installed within the image: -### TractusX-EDC Control Plane +## TractusX-EDC Control Plane - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx - Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) -**Used base image** +## Used base image - [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin @@ -26,4 +26,3 @@ from the base distribution, along with any direct or indirect dependencies of th As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. - diff --git a/edc-controlplane/edc-controlplane-memory/notice.md b/edc-controlplane/edc-controlplane-memory/notice.md index 5806989b4..d8bcac50b 100644 --- a/edc-controlplane/edc-controlplane-memory/notice.md +++ b/edc-controlplane/edc-controlplane-memory/notice.md @@ -1,4 +1,4 @@ -## Notice for Docker image +# Notice for Docker image An EDC Control Plane using memory-based storage, and Azure KeyVault as secret store. @@ -6,14 +6,14 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory Eclipse Tractus-X product(s) installed within the image: -### TractusX-EDC Control Plane +## TractusX-EDC Control Plane - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx - Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) -**Used base image** +## Used base image - [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin @@ -26,4 +26,3 @@ from the base distribution, along with any direct or indirect dependencies of th As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. - diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md index ac8288747..cd46028a5 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md @@ -1,4 +1,4 @@ -## Notice for Docker image +# Notice for Docker image An EDC Control Plane using PostgreSQL as persistence backend, and HashiCorp Vault as secret store. @@ -6,14 +6,14 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql-hashico Eclipse Tractus-X product(s) installed within the image: -### TractusX-EDC Control Plane +## TractusX-EDC Control Plane - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx - Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) -**Used base image** +## Used base image - [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin @@ -26,4 +26,3 @@ from the base distribution, along with any direct or indirect dependencies of th As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. - diff --git a/edc-controlplane/edc-controlplane-postgresql/notice.md b/edc-controlplane/edc-controlplane-postgresql/notice.md index edb15438d..a73966fa9 100644 --- a/edc-controlplane/edc-controlplane-postgresql/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql/notice.md @@ -1,4 +1,4 @@ -## Notice for Docker image +# Notice for Docker image An EDC Control Plane using PostgreSQL as persistence backend, and Azure KeyVault as secret store. @@ -6,14 +6,14 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql Eclipse Tractus-X product(s) installed within the image: -### TractusX-EDC Control Plane +## TractusX-EDC Control Plane - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx - Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) -**Used base image** +## Used base image - [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin @@ -26,4 +26,3 @@ from the base distribution, along with any direct or indirect dependencies of th As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. - diff --git a/edc-dataplane/edc-dataplane-azure-vault/notice.md b/edc-dataplane/edc-dataplane-azure-vault/notice.md index a161899ef..5c95dfd5b 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/notice.md +++ b/edc-dataplane/edc-dataplane-azure-vault/notice.md @@ -1,4 +1,4 @@ -## Notice for Docker image +# Notice for Docker image An EDC Data Plane using the Azure KeyVault. @@ -6,14 +6,14 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-dataplane-azure-vault Eclipse Tractus-X product(s) installed within the image: -### TractusX-EDC Data Plane +## TractusX-EDC Data Plane - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx - Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) -**Used base image** +## Used base image - [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin @@ -26,4 +26,3 @@ from the base distribution, along with any direct or indirect dependencies of th As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. - diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md index 90c281a2f..f734642ad 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md @@ -1,4 +1,4 @@ -## Notice for Docker image +# Notice for Docker image An EDC Data Plane using the HashiCorp Vault @@ -6,14 +6,14 @@ DockerHub: https://hub.docker.com/r/tractusx/edc-dataplane-hashicorp-vault Eclipse Tractus-X product(s) installed within the image: -### TractusX-EDC Data Plane +## TractusX-EDC Data Plane - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx - Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) -**Used base image** +## Used base image - [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin @@ -26,4 +26,3 @@ from the base distribution, along with any direct or indirect dependencies of th As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. - From cbed53492212f9ae8847733fc636a044f1c443a8 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 17:32:58 +0200 Subject: [PATCH 064/132] fix workflow --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 660600a73..34a003d31 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -87,7 +87,7 @@ jobs: build-controlplane: name: "Create Docker Images for the ControlPlane" runs-on: ubuntu-latest - needs: [ secrets-presence ] + needs: [ secret-presence ] strategy: fail-fast: false matrix: From f036a59b05c7399a4d7eb838afc272e175dbf8ff Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 17:35:30 +0200 Subject: [PATCH 065/132] remove image namespace --- .github/workflows/build.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 34a003d31..24b6ff01e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -105,7 +105,6 @@ jobs: with: rootDir: edc-controlplane/${{ matrix.name }} imagename: ${{ matrix.name }} - namespace: ${{ inputs.namespace }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} @@ -127,7 +126,6 @@ jobs: with: rootDir: edc-dataplane/${{ matrix.name }} imagename: ${{ matrix.name }} - namespace: ${{ inputs.namespace }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} From fe6891e3ea11fd73854607d4e199d2e87c664642 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 12 Apr 2023 17:54:05 +0200 Subject: [PATCH 066/132] prevent all interaction with dockerhub on pull requests --- .github/actions/publish-docker-image/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 7cbe07297..0e1fee7c4 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -104,7 +104,7 @@ runs: # https://github.com/peter-evans/dockerhub-description ############################### - name: Update Docker Hub description - if: github.event_name != 'pull_request' && ${{ inputs.docker_user }} && ${{ inputs.docker_token }} + if: github.event_name != 'pull_request' uses: peter-evans/dockerhub-description@v3 with: readme-filepath: ${{ inputs.rootDir }}/notice.md From d38c16364822d6ddd98d1368af03b19867e3f1ef Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 13 Apr 2023 16:38:57 +0200 Subject: [PATCH 067/132] docs: add technical committer to pr_etiquette.md (#182) --- pr_etiquette.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pr_etiquette.md b/pr_etiquette.md index ce9ec73f8..aaaf16761 100644 --- a/pr_etiquette.md +++ b/pr_etiquette.md @@ -58,6 +58,13 @@ Submitting pull requests in EDC should be done while adhering to a couple of sim - Be civil and objective. No foul language, insulting or otherwise abusive language will be tolerated. The goal is to _encourage_ contributions. -## The technical committers +## The technical committers (as of April 05, 2023) -- TBD +Main committers for the TractusX-EDC project: + +- @paullatzelsperger +- @florianrusch-zf + +Alternatively, the following Tractus-X committers can also step in: + +- @SebastianBezold From 79789ba82a11fbd7c8116e70cd806b4e38267780 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Fri, 14 Apr 2023 10:27:58 +0200 Subject: [PATCH 068/132] chore: update to temurin 17 (#212) * chore: update dockerfiles and GH Actions to temurin 17 * pin specific version --- .github/actions/publish-docker-image/action.yml | 2 +- .github/workflows/build.yaml | 4 ++-- .github/workflows/business-tests.yaml | 2 +- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/publish-new-release.yml | 4 ++-- .github/workflows/veracode.yaml | 6 +++--- .github/workflows/verify.yaml | 12 ++++++------ .../notice.md | 2 +- .../src/main/docker/Dockerfile | 2 +- edc-controlplane/edc-controlplane-memory/notice.md | 2 +- .../src/main/docker/Dockerfile | 2 +- .../notice.md | 2 +- .../src/main/docker/Dockerfile | 2 +- .../edc-controlplane-postgresql/notice.md | 2 +- .../src/main/docker/Dockerfile | 2 +- edc-dataplane/edc-dataplane-azure-vault/notice.md | 2 +- .../src/main/docker/Dockerfile | 2 +- .../edc-dataplane-hashicorp-vault/notice.md | 2 +- .../src/main/docker/Dockerfile | 2 +- 19 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 4cd57dfe8..0dfefd5f0 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -60,7 +60,7 @@ runs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' - name: Build Controlplane diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5b37d55b4..f3e63414a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -73,7 +73,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' # Build @@ -147,7 +147,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' - name: Import GPG Key diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 9273b781c..7c64b29f5 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -55,7 +55,7 @@ jobs: name: Set-Up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' - diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 915f2b7a8..82e956c76 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -36,7 +36,7 @@ jobs: name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' - diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index e3d4d555f..c626bba84 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -60,7 +60,7 @@ jobs: name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' @@ -181,7 +181,7 @@ jobs: name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' - diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index b980a2829..a58d3fa4d 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -33,7 +33,7 @@ jobs: name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' - @@ -62,7 +62,7 @@ jobs: name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' # Build @@ -111,7 +111,7 @@ jobs: name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' # Build diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index 5cdaa5c9c..01064f35d 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -63,7 +63,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' - name: Verify proper formatting @@ -96,7 +96,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' @@ -113,7 +113,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' @@ -130,7 +130,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' @@ -147,7 +147,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' @@ -168,7 +168,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: - java-version: '11' + java-version: '17' distribution: 'temurin' cache: 'gradle' - name: Cache SonarCloud packages diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md index cf6aa8d92..fdcc88583 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md @@ -15,7 +15,7 @@ Eclipse Tractus-X product(s) installed within the image: ## Used base image -- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin - Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin - Additional information about the Eclipse Temurin diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile index 229c44868..149256182 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-controlplane/edc-controlplane-memory/notice.md b/edc-controlplane/edc-controlplane-memory/notice.md index d8bcac50b..cee9fe5ed 100644 --- a/edc-controlplane/edc-controlplane-memory/notice.md +++ b/edc-controlplane/edc-controlplane-memory/notice.md @@ -15,7 +15,7 @@ Eclipse Tractus-X product(s) installed within the image: ## Used base image -- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin - Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin - Additional information about the Eclipse Temurin diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile index c9af99a81..d248e8131 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md index cd46028a5..3b5e517f0 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md @@ -15,7 +15,7 @@ Eclipse Tractus-X product(s) installed within the image: ## Used base image -- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin - Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin - Additional information about the Eclipse Temurin diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index c9af99a81..d248e8131 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-controlplane/edc-controlplane-postgresql/notice.md b/edc-controlplane/edc-controlplane-postgresql/notice.md index a73966fa9..d9e1b58b1 100644 --- a/edc-controlplane/edc-controlplane-postgresql/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql/notice.md @@ -15,7 +15,7 @@ Eclipse Tractus-X product(s) installed within the image: ## Used base image -- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin - Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin - Additional information about the Eclipse Temurin diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index c9af99a81..d248e8131 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-dataplane/edc-dataplane-azure-vault/notice.md b/edc-dataplane/edc-dataplane-azure-vault/notice.md index 5c95dfd5b..7023f7ab7 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/notice.md +++ b/edc-dataplane/edc-dataplane-azure-vault/notice.md @@ -15,7 +15,7 @@ Eclipse Tractus-X product(s) installed within the image: ## Used base image -- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin - Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin - Additional information about the Eclipse Temurin diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index 5c9e65d5e..cb3e3f817 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md index f734642ad..8b18d0a4b 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md @@ -15,7 +15,7 @@ Eclipse Tractus-X product(s) installed within the image: ## Used base image -- [eclipse-temurin:11.0.18_10-jre-alpine](https://github.com/adoptium/containers) +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) - Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin - Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin - Additional information about the Eclipse Temurin diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 5c9e65d5e..cb3e3f817 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker From 0ac05fe9793520ddb20f4debb6d236abbb27741c Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Fri, 14 Apr 2023 10:40:35 +0200 Subject: [PATCH 069/132] feat(tests): removes lombok from edc-tests module (#159) --- .../eclipse/tractusx/edc/tests/Connector.java | 55 +- .../tractusx/edc/tests/ConnectorFactory.java | 27 +- .../eclipse/tractusx/edc/tests/Constants.java | 25 +- .../edc/tests/ControlPlaneAdapterSteps.java | 82 +- .../tractusx/edc/tests/DataManagementAPI.java | 1309 +++++++++-------- .../tractusx/edc/tests/Environment.java | 203 ++- .../edc/tests/HttpProxyTransferSteps.java | 6 +- .../tractusx/edc/tests/NegotiationSteps.java | 89 +- .../tractusx/edc/tests/PolicyStepDefs.java | 65 +- .../edc/tests/S3FileTransferStepsDefs.java | 227 ++- .../tractusx/edc/tests/data/Asset.java | 26 +- .../data/BusinessPartnerNumberConstraint.java | 14 +- .../edc/tests/data/ContractDefinition.java | 41 +- .../edc/tests/data/ContractNegotiation.java | 29 +- .../edc/tests/data/ContractOffer.java | 28 +- .../data/HttpProxySourceDataAddress.java | 60 +- .../tractusx/edc/tests/data/Negotiation.java | 57 +- .../tractusx/edc/tests/data/OrConstraint.java | 15 +- .../edc/tests/data/PayMeConstraint.java | 14 +- .../tractusx/edc/tests/data/Permission.java | 29 +- .../tractusx/edc/tests/data/Policy.java | 22 +- .../edc/tests/data/S3DataAddress.java | 28 +- .../tractusx/edc/tests/data/Transfer.java | 46 +- .../edc/tests/data/TransferProcess.java | 21 +- .../edc/tests/util/DatabaseCleaner.java | 45 +- .../tractusx/edc/tests/util/S3Client.java | 166 +-- 26 files changed, 1577 insertions(+), 1152 deletions(-) diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java index 5c7a42c5d..d4e2ea7a8 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java @@ -20,36 +20,56 @@ package org.eclipse.tractusx.edc.tests; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; + import org.eclipse.tractusx.edc.tests.util.DatabaseCleaner; import org.eclipse.tractusx.edc.tests.util.S3Client; import static org.mockito.Mockito.mock; -@RequiredArgsConstructor public class Connector { - @NonNull - @Getter private final String name; - @Getter - @NonNull private final Environment environment; - @Getter(lazy = true) - private final DataManagementAPI dataManagementAPI = loadDataManagementAPI(); + private final DataManagementAPI dataManagementAPI; + + private final DatabaseCleaner databaseCleaner; + + + private final S3Client s3Client; + + public Connector(String name, Environment environment) { + this.name = name; + this.environment = environment; + dataManagementAPI = loadDataManagementAPI(); + databaseCleaner = loadDatabaseCleaner(); + s3Client = createS3Client(); + } + + public BackendDataService getBackendServiceBackendAPI() { + return mock(BackendDataService.class); + } + + public DatabaseCleaner getDatabaseCleaner() { + return databaseCleaner; + } - @Getter(lazy = true) - private final BackendDataService backendServiceBackendAPI = loadBackendServiceBackendAPI(); + public DataManagementAPI getDataManagementAPI() { + return dataManagementAPI; + } - @Getter(lazy = true) - private final DatabaseCleaner databaseCleaner = loadDatabaseCleaner(); + public Environment getEnvironment() { + return environment; + } - @Getter(lazy = true) - private final S3Client s3Client = createS3Client(); + public S3Client getS3Client() { + return s3Client; + } + + public String getName() { + return name; + } private DataManagementAPI loadDataManagementAPI() { return new DataManagementAPI( @@ -63,9 +83,6 @@ private DatabaseCleaner loadDatabaseCleaner() { environment.getDatabasePassword()); } - private BackendDataService loadBackendServiceBackendAPI() { - return mock(BackendDataService.class); - } private S3Client createS3Client() { return new S3Client(environment); diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java index 7a8ef81a1..364e266f3 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java @@ -20,24 +20,25 @@ package org.eclipse.tractusx.edc.tests; -import java.util.HashMap; import java.util.Locale; import java.util.Map; -import lombok.NonNull; -import lombok.experimental.UtilityClass; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + -@UtilityClass public class ConnectorFactory { - private static final Map CONNECTOR_CACHE = new HashMap<>(); + private static final Map CONNECTOR_CACHE = new ConcurrentHashMap<>(); - public static Connector byName(@NonNull final String name) { - return CONNECTOR_CACHE.computeIfAbsent( - name.toUpperCase(Locale.ROOT), k -> createConnector(name)); - } + public static Connector byName(String name) { + Objects.requireNonNull(name); + return CONNECTOR_CACHE.computeIfAbsent( + name.toUpperCase(Locale.ROOT), k -> createConnector(name)); + } - private static Connector createConnector(@NonNull final String name) { - final Environment environment = Environment.byName(name); + private static Connector createConnector(String name) { + Objects.requireNonNull(name); + Environment environment = Environment.byName(name); - return new Connector(name, environment); - } + return new Connector(name, environment); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java index 67b484e38..6a7de2ceb 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java @@ -20,19 +20,16 @@ package org.eclipse.tractusx.edc.tests; -import lombok.experimental.UtilityClass; - -@UtilityClass public final class Constants { - public static final String DATA_MANAGEMENT_URL = "DATA_MANAGEMENT_URL"; - public static final String DATA_MANAGEMENT_API_AUTH_KEY = "DATA_MANAGEMENT_API_AUTH_KEY"; - public static final String IDS_URL = "IDS_URL"; - public static final String DATA_PLANE_URL = "DATA_PLANE_URL"; - public static final String BACKEND_SERVICE_BACKEND_API_URL = "BACKEND_SERVICE_BACKEND_API_URL"; - public static final String DATABASE_URL = "DATABASE_URL"; - public static final String DATABASE_USER = "DATABASE_USER"; - public static final String DATABASE_PASSWORD = "DATABASE_PASSWORD"; - public static final String EDC_AWS_ENDPOINT_OVERRIDE = "EDC_AWS_ENDPOINT_OVERRIDE"; - public static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"; - public static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; + public static final String DATA_MANAGEMENT_URL = "DATA_MANAGEMENT_URL"; + public static final String DATA_MANAGEMENT_API_AUTH_KEY = "DATA_MANAGEMENT_API_AUTH_KEY"; + public static final String IDS_URL = "IDS_URL"; + public static final String DATA_PLANE_URL = "DATA_PLANE_URL"; + public static final String BACKEND_SERVICE_BACKEND_API_URL = "BACKEND_SERVICE_BACKEND_API_URL"; + public static final String DATABASE_URL = "DATABASE_URL"; + public static final String DATABASE_USER = "DATABASE_USER"; + public static final String DATABASE_PASSWORD = "DATABASE_PASSWORD"; + public static final String EDC_AWS_ENDPOINT_OVERRIDE = "EDC_AWS_ENDPOINT_OVERRIDE"; + public static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"; + public static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ControlPlaneAdapterSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ControlPlaneAdapterSteps.java index d29a13aa4..e786c789a 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ControlPlaneAdapterSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ControlPlaneAdapterSteps.java @@ -22,64 +22,66 @@ import com.google.gson.Gson; import io.cucumber.datatable.DataTable; -import java.io.IOException; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.HttpClientBuilder; import org.eclipse.edc.spi.system.health.HealthStatus; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Map; -@Slf4j public class ControlPlaneAdapterSteps { - private EndpointDataReference endpointDataReference; + private static final Logger log = LoggerFactory.getLogger(ControlPlaneAdapterSteps.class); + private EndpointDataReference endpointDataReference; - /* - * TODO: see of EndToEndTransfer.feature - * the current Bussinnes test is not running, because of a possible rare condition in the CI pipeline - * regarding the contract validity: see https://github.com/eclipse-edc/Connector/issues/2514 - */ + /* + * TODO: see of EndToEndTransfer.feature + * the current Bussinnes test is not running, because of a possible rare condition in the CI pipeline + * regarding the contract validity: see https://github.com/eclipse-edc/Connector/issues/2514 + */ - // @When("'{connector}' gets a request endpoint from '{connector}'") - public void getEndPointFromGetRequest(Connector consumer, Connector receiver, DataTable table) - throws IOException { + // @When("'{connector}' gets a request endpoint from '{connector}'") + public void getEndPointFromGetRequest(Connector consumer, Connector receiver, DataTable table) + throws IOException { - final DataManagementAPI dataManagementAPI = consumer.getDataManagementAPI(); - final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + DataManagementAPI dataManagementAPI = consumer.getDataManagementAPI(); + String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - for (Map map : table.asMaps()) { - final String assetId = map.get("asset id"); + for (Map map : table.asMaps()) { + String assetId = map.get("asset id"); - endpointDataReference = dataManagementAPI.getEdcEndpoint(assetId, receiverIdsUrl); + endpointDataReference = dataManagementAPI.getEdcEndpoint(assetId, receiverIdsUrl); - log.debug("endpointDataReference in controlplane" + endpointDataReference.toString()); + log.debug("endpointDataReference in controlplane" + endpointDataReference.toString()); + } } - } - /* - * TODO: see EndToEndTransfer.feature - * the current Bussinnes test is not running, because of a possible rare condition in the CI pipeline - * regarding the contract validity: see https://github.com/eclipse-edc/Connector/issues/2514 - */ + /* + * TODO: see EndToEndTransfer.feature + * the current Bussinnes test is not running, because of a possible rare condition in the CI pipeline + * regarding the contract validity: see https://github.com/eclipse-edc/Connector/issues/2514 + */ - // @Then("'{connector}' asks for the asset from the endpoint") - public void receiveEndpoint(Connector consumer) throws IOException { + // @Then("'{connector}' asks for the asset from the endpoint") + public void receiveEndpoint(Connector consumer) throws IOException { - var requestUrl = endpointDataReference.getEndpoint(); - var key = endpointDataReference.getAuthKey(); - var value = endpointDataReference.getAuthCode(); - var httpClient = HttpClientBuilder.create().build(); - var get = new HttpGet(requestUrl); - get.addHeader(key, value); - final CloseableHttpResponse response = httpClient.execute(get); - var bytes = response.getEntity().getContent().readAllBytes(); - var result = new String(bytes); - var resultTransformed = new Gson().fromJson(result, HealthStatus.class); + var requestUrl = endpointDataReference.getEndpoint(); + var key = endpointDataReference.getAuthKey(); + var value = endpointDataReference.getAuthCode(); + var httpClient = HttpClientBuilder.create().build(); + var get = new HttpGet(requestUrl); + get.addHeader(key, value); + CloseableHttpResponse response = httpClient.execute(get); + var bytes = response.getEntity().getContent().readAllBytes(); + var result = new String(bytes); + var resultTransformed = new Gson().fromJson(result, HealthStatus.class); - Assertions.assertTrue(resultTransformed.isHealthy()); - Assertions.assertFalse(resultTransformed.getComponentResults().isEmpty()); - } + Assertions.assertTrue(resultTransformed.isHealthy()); + Assertions.assertFalse(resultTransformed.getComponentResults().isEmpty()); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java index 735cbf175..d67dc77ca 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java @@ -23,17 +23,6 @@ import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.Data; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; @@ -45,628 +34,706 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicHeader; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.tests.data.*; +import org.eclipse.tractusx.edc.tests.data.Asset; +import org.eclipse.tractusx.edc.tests.data.BusinessPartnerNumberConstraint; +import org.eclipse.tractusx.edc.tests.data.Constraint; +import org.eclipse.tractusx.edc.tests.data.ContractDefinition; +import org.eclipse.tractusx.edc.tests.data.ContractNegotiation; +import org.eclipse.tractusx.edc.tests.data.ContractNegotiationState; +import org.eclipse.tractusx.edc.tests.data.ContractOffer; +import org.eclipse.tractusx.edc.tests.data.DataAddress; +import org.eclipse.tractusx.edc.tests.data.HttpProxySinkDataAddress; +import org.eclipse.tractusx.edc.tests.data.HttpProxySourceDataAddress; +import org.eclipse.tractusx.edc.tests.data.Negotiation; +import org.eclipse.tractusx.edc.tests.data.NullDataAddress; +import org.eclipse.tractusx.edc.tests.data.OrConstraint; +import org.eclipse.tractusx.edc.tests.data.PayMeConstraint; +import org.eclipse.tractusx.edc.tests.data.Permission; +import org.eclipse.tractusx.edc.tests.data.Policy; +import org.eclipse.tractusx.edc.tests.data.S3DataAddress; +import org.eclipse.tractusx.edc.tests.data.Transfer; +import org.eclipse.tractusx.edc.tests.data.TransferProcess; +import org.eclipse.tractusx.edc.tests.data.TransferProcessState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; -@Slf4j public class DataManagementAPI { - private static final String ASSET_PATH = "/assets"; - private static final String POLICY_PATH = "/policydefinitions"; - private static final String CONTRACT_DEFINITIONS_PATH = "/contractdefinitions"; - private static final String CATALOG_PATH = "/catalog"; - private static final String NEGOTIATIONS_PATH = "/contractnegotiations"; - private static final String TRANSFER_PATH = "/transferprocess"; - private static final String ADAPTER_PATH = "/adapter/asset/sync/"; - - private final String dataMgmtUrl; - private final String dataMgmtAuthKey; - private final CloseableHttpClient httpClient; - - public DataManagementAPI(String dataManagementUrl, String dataMgmtAuthKey) { - this.httpClient = HttpClientBuilder.create().build(); - this.dataMgmtUrl = dataManagementUrl; - this.dataMgmtAuthKey = dataMgmtAuthKey; - } - - public List requestCatalogFrom(String receivingConnectorUrl) throws IOException { - final String encodedUrl = URLEncoder.encode(receivingConnectorUrl, StandardCharsets.UTF_8); - final ManagementApiContractOfferCatalog catalog = - get( - CATALOG_PATH, - "providerUrl=" + encodedUrl, - new TypeToken() {}); - - log.debug("Received " + catalog.contractOffers.size() + " offers"); - - return catalog.contractOffers.stream().map(this::mapOffer).collect(Collectors.toList()); - } - - public Negotiation initiateNegotiation( - String receivingConnectorUrl, String definitionId, String assetId, Policy policy) - throws IOException { - final ManagementApiOffer offer = new ManagementApiOffer(); - offer.offerId = definitionId + ":foo"; - offer.assetId = assetId; - offer.policy = mapPolicy(policy); - offer.policy.permissions.forEach(p -> p.target = assetId); - - final ManagementApiNegotiationPayload negotiationPayload = - new ManagementApiNegotiationPayload(); - negotiationPayload.connectorAddress = receivingConnectorUrl; - negotiationPayload.offer = offer; - - final ManagementApiNegotiationResponse response = - post( - NEGOTIATIONS_PATH, - negotiationPayload, - new TypeToken() {}); - - if (response == null) - throw new RuntimeException( - "Initiated negotiation. Connector did not answer with negotiation ID."); - - log.info(String.format("Initiated negotiation (id=%s)", response.getId())); - - final String negotiationId = response.getId(); - return new Negotiation(negotiationId); - } - - public Transfer initiateTransferProcess( - String receivingConnectorUrl, - String contractAgreementId, - String assetId, - DataAddress dataAddress) - throws IOException { - final ManagementApiTransfer transfer = new ManagementApiTransfer(); - - transfer.connectorAddress = receivingConnectorUrl; - transfer.contractId = contractAgreementId; - transfer.assetId = assetId; - transfer.transferType = new ManagementApiTransferType(); - transfer.managedResources = false; - transfer.dataDestination = mapDataAddress(dataAddress); - transfer.protocol = "ids-multipart"; - - return initiateTransferProcess(transfer); - } - - public Transfer initiateTransferProcess( - String receivingConnectorUrl, - String contractAgreementId, - String assetId, - DataAddress dataAddress, - String receiverEndpoint) - throws IOException { - final ManagementApiTransfer transfer = new ManagementApiTransfer(); - - transfer.connectorAddress = receivingConnectorUrl; - transfer.contractId = contractAgreementId; - transfer.assetId = assetId; - transfer.transferType = new ManagementApiTransferType(); - transfer.managedResources = false; - transfer.dataDestination = mapDataAddress(dataAddress); - transfer.protocol = "ids-multipart"; - transfer.properties = new ManagementApiProperties(receiverEndpoint); - - return initiateTransferProcess(transfer); - } - - private Transfer initiateTransferProcess(ManagementApiTransfer transfer) throws IOException { - final ManagementApiTransferResponse response = - post(TRANSFER_PATH, transfer, new TypeToken() {}); - - if (response == null) - throw new RuntimeException( - "Initiated transfer process. Connector did not answer with transfer process ID."); - - log.info(String.format("Initiated transfer process (id=%s)", response.getId())); - - final String transferId = response.getId(); - return new Transfer(transferId); - } - - public Asset initiateTransferProcess( - String endpointUrl, String endpointAuthKey, String endpointAuthCode) throws IOException { - Header header = new BasicHeader(endpointAuthKey, endpointAuthCode); - return get(endpointUrl, header, new TypeToken() {}); - } - - public TransferProcess getTransferProcess(String id) throws IOException { - final ManagementApiTransferProcess transferProcess = - get(TRANSFER_PATH + "/" + id, new TypeToken() {}); - return mapTransferProcess(transferProcess); - } - - public ContractNegotiation getNegotiation(String id) throws IOException { - final ManagementApiNegotiation negotiation = - get(NEGOTIATIONS_PATH + "/" + id, new TypeToken() {}); - return mapNegotiation(negotiation); - } - - public List getNegotiations() throws IOException { - final List negotiations = - get(NEGOTIATIONS_PATH + "/", new TypeToken>() {}); - return negotiations.stream().map(this::mapNegotiation).collect(Collectors.toList()); - } + private static final Logger log = LoggerFactory.getLogger(DataManagementAPI.class); + private static final String ASSET_PATH = "/assets"; + private static final String POLICY_PATH = "/policydefinitions"; + private static final String CONTRACT_DEFINITIONS_PATH = "/contractdefinitions"; + private static final String CATALOG_PATH = "/catalog"; + private static final String NEGOTIATIONS_PATH = "/contractnegotiations"; + private static final String TRANSFER_PATH = "/transferprocess"; + private static final String ADAPTER_PATH = "/adapter/asset/sync/"; + + private final String dataMgmtUrl; + private final String dataMgmtAuthKey; + private final CloseableHttpClient httpClient; + + public DataManagementAPI(String dataManagementUrl, String dataMgmtAuthKey) { + httpClient = HttpClientBuilder.create().build(); + dataMgmtUrl = dataManagementUrl; + this.dataMgmtAuthKey = dataMgmtAuthKey; + } + + public List requestCatalogFrom(String receivingConnectorUrl) throws IOException { + String encodedUrl = URLEncoder.encode(receivingConnectorUrl, StandardCharsets.UTF_8); + ManagementApiContractOfferCatalog catalog = + get( + CATALOG_PATH, + "providerUrl=" + encodedUrl, + new TypeToken() { + }); + + log.debug("Received " + catalog.contractOffers.size() + " offers"); + + return catalog.contractOffers.stream().map(this::mapOffer).collect(Collectors.toList()); + } + + public Negotiation initiateNegotiation( + String receivingConnectorUrl, String definitionId, String assetId, Policy policy) + throws IOException { + ManagementApiOffer offer = new ManagementApiOffer(); + offer.offerId = definitionId + ":foo"; + offer.assetId = assetId; + offer.policy = mapPolicy(policy); + offer.policy.permissions.forEach(p -> p.target = assetId); + + ManagementApiNegotiationPayload negotiationPayload = + new ManagementApiNegotiationPayload(); + negotiationPayload.connectorAddress = receivingConnectorUrl; + negotiationPayload.offer = offer; + + ManagementApiNegotiationResponse response = + post( + NEGOTIATIONS_PATH, + negotiationPayload, + new TypeToken() { + }); + + if (response == null) { + throw new RuntimeException( + "Initiated negotiation. Connector did not answer with negotiation ID."); + } + + log.info(String.format("Initiated negotiation (id=%s)", response.getId())); + + String negotiationId = response.getId(); + return new Negotiation(negotiationId); + } + + public Transfer initiateTransferProcess( + String receivingConnectorUrl, + String contractAgreementId, + String assetId, + DataAddress dataAddress) + throws IOException { + ManagementApiTransfer transfer = new ManagementApiTransfer(); + + transfer.connectorAddress = receivingConnectorUrl; + transfer.contractId = contractAgreementId; + transfer.assetId = assetId; + transfer.transferType = new ManagementApiTransferType(); + transfer.managedResources = false; + transfer.dataDestination = mapDataAddress(dataAddress); + transfer.protocol = "ids-multipart"; + + return initiateTransferProcess(transfer); + } + + public Transfer initiateTransferProcess( + String receivingConnectorUrl, + String contractAgreementId, + String assetId, + DataAddress dataAddress, + String receiverEndpoint) + throws IOException { + ManagementApiTransfer transfer = new ManagementApiTransfer(); + + transfer.connectorAddress = receivingConnectorUrl; + transfer.contractId = contractAgreementId; + transfer.assetId = assetId; + transfer.transferType = new ManagementApiTransferType(); + transfer.managedResources = false; + transfer.dataDestination = mapDataAddress(dataAddress); + transfer.protocol = "ids-multipart"; + transfer.properties = new ManagementApiProperties(receiverEndpoint); + + return initiateTransferProcess(transfer); + } + + public Asset initiateTransferProcess( + String endpointUrl, String endpointAuthKey, String endpointAuthCode) throws IOException { + Header header = new BasicHeader(endpointAuthKey, endpointAuthCode); + return get(endpointUrl, header, new TypeToken() { + }); + } + + public TransferProcess getTransferProcess(String id) throws IOException { + ManagementApiTransferProcess transferProcess = + get(TRANSFER_PATH + "/" + id, new TypeToken() { + }); + return mapTransferProcess(transferProcess); + } + + public ContractNegotiation getNegotiation(String id) throws IOException { + ManagementApiNegotiation negotiation = + get(NEGOTIATIONS_PATH + "/" + id, new TypeToken() { + }); + return mapNegotiation(negotiation); + } + + public List getNegotiations() throws IOException { + List negotiations = + get(NEGOTIATIONS_PATH + "/", new TypeToken>() { + }); + return negotiations.stream().map(this::mapNegotiation).collect(Collectors.toList()); + } + + public void createAsset(Asset asset) throws IOException { + ManagementApiAssetCreate assetCreate = new ManagementApiAssetCreate(); + + assetCreate.asset = mapAsset(asset); + assetCreate.dataAddress = mapDataAddress(asset.getDataAddress()); + + post(ASSET_PATH, assetCreate); + } + + public void createPolicy(Policy policy) throws IOException { + post(POLICY_PATH, mapPolicyDefinition(policy)); + } + + public void createContractDefinition(ContractDefinition contractDefinition) throws IOException { + post(CONTRACT_DEFINITIONS_PATH, mapContractDefinition(contractDefinition)); + } + + public EndpointDataReference getEdcEndpoint(String assetId, String receivingConnectorUrl) + throws IOException { + String encodedUrl = ADAPTER_PATH + assetId + "?providerUrl=" + receivingConnectorUrl; + + EndpointDataReference endpoint = + get(encodedUrl, new TypeToken() { + }); + + return endpoint; + } + + private Transfer initiateTransferProcess(ManagementApiTransfer transfer) throws IOException { + ManagementApiTransferResponse response = + post(TRANSFER_PATH, transfer, new TypeToken() { + }); + + if (response == null) { + throw new RuntimeException( + "Initiated transfer process. Connector did not answer with transfer process ID."); + } - public void createAsset(Asset asset) throws IOException { - final ManagementApiAssetCreate assetCreate = new ManagementApiAssetCreate(); + log.info(String.format("Initiated transfer process (id=%s)", response.getId())); - assetCreate.asset = mapAsset(asset); - assetCreate.dataAddress = mapDataAddress(asset.getDataAddress()); + String transferId = response.getId(); + return new Transfer(transferId); + } + + private T get(String path, String params, TypeToken typeToken) throws IOException { + return get(path + "?" + params, typeToken); + } + + private T get(String path, TypeToken typeToken) throws IOException { + + HttpGet get = new HttpGet(dataMgmtUrl + path); + HttpResponse response = sendRequest(get); + byte[] json = response.getEntity().getContent().readAllBytes(); + + log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); + return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); + } + + private T get(String path, Header header, TypeToken typeToken) throws IOException { + + HttpGet get = new HttpGet(path); + get.addHeader(header); + HttpResponse response = sendRequest(get); + byte[] json = response.getEntity().getContent().readAllBytes(); + + log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); + return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); + } + + private void post(String path, Object object) throws IOException { + post(path, object, new TypeToken() { + }); + } + + private T post(String path, Object object, TypeToken typeToken) throws IOException { + String url = String.format("%s%s", dataMgmtUrl, path); + HttpPost post = new HttpPost(url); + post.addHeader("Content-Type", "application/json"); + + var json = new Gson().toJson(object); + + log.debug("POST Payload: " + json); + + post.setEntity(new StringEntity(json)); + CloseableHttpResponse response = sendRequest(post); + + T responseJson = null; + if (!typeToken.equals(new TypeToken() { + })) { + byte[] responseBytes = response.getEntity().getContent().readAllBytes(); + responseJson = + new Gson() + .fromJson(new String(responseBytes, StandardCharsets.UTF_8), typeToken.getType()); + } + + response.close(); + + return responseJson; + } + + private CloseableHttpResponse sendRequest(HttpRequestBase request) throws IOException { + request.addHeader("X-Api-Key", dataMgmtAuthKey); + + log.debug(String.format("Send %-6s %s", request.getMethod(), request.getURI())); + + CloseableHttpResponse response = httpClient.execute(request); + if (200 > response.getStatusLine().getStatusCode() + || response.getStatusLine().getStatusCode() >= 300) { + throw new RuntimeException( + String.format("Unexpected response: %s", response.getStatusLine())); + } + + return response; + } + + private ContractNegotiation mapNegotiation(ManagementApiNegotiation negotiation) { + + ContractNegotiationState state; + + switch (negotiation.state) { + case "ERROR": + state = ContractNegotiationState.ERROR; + break; + case "INITIAL": + state = ContractNegotiationState.INITIAL; + break; + case "DECLINED": + state = ContractNegotiationState.DECLINED; + break; + case "CONFIRMED": + state = ContractNegotiationState.CONFIRMED; + break; + default: + state = ContractNegotiationState.UNKNOWN; + } + + return new ContractNegotiation(negotiation.id, state, negotiation.contractAgreementId); + } + + private TransferProcess mapTransferProcess(ManagementApiTransferProcess transferProcess) { - post(ASSET_PATH, assetCreate); - } - - public void createPolicy(Policy policy) throws IOException { - post(POLICY_PATH, mapPolicyDefinition(policy)); - } - - public void createContractDefinition(ContractDefinition contractDefinition) throws IOException { - post(CONTRACT_DEFINITIONS_PATH, mapContractDefinition(contractDefinition)); - } - - public EndpointDataReference getEdcEndpoint(String assetId, String receivingConnectorUrl) - throws IOException { - final String encodedUrl = ADAPTER_PATH + assetId + "?providerUrl=" + receivingConnectorUrl; - - final EndpointDataReference endpoint = - get(encodedUrl, new TypeToken() {}); + TransferProcessState state; - return endpoint; - } + switch (transferProcess.state) { + case "COMPLETED": + state = TransferProcessState.COMPLETED; + break; + case "ERROR": + state = TransferProcessState.ERROR; + break; + default: + state = TransferProcessState.UNKNOWN; + } - private T get(String path, String params, TypeToken typeToken) throws IOException { - return get(path + "?" + params, typeToken); - } - - private T get(String path, TypeToken typeToken) throws IOException { - - final HttpGet get = new HttpGet(dataMgmtUrl + path); - final HttpResponse response = sendRequest(get); - final byte[] json = response.getEntity().getContent().readAllBytes(); + return new TransferProcess(transferProcess.id, state); + } + + private ManagementApiDataAddress mapDataAddress(DataAddress dataAddress) { + Objects.requireNonNull(dataAddress); + ManagementApiDataAddress apiObject = new ManagementApiDataAddress(); + + if (dataAddress instanceof HttpProxySourceDataAddress) { + var address = (HttpProxySourceDataAddress) dataAddress; + var properties = new HashMap(); + properties.put("type", "HttpData"); + properties.put("baseUrl", address.getBaseUrl()); + var oauth2Provision = address.getOauth2Provision(); + if (oauth2Provision != null) { + properties.put("oauth2:tokenUrl", oauth2Provision.getTokenUrl()); + properties.put("oauth2:clientId", oauth2Provision.getClientId()); + properties.put("oauth2:clientSecret", oauth2Provision.getClientSecret()); + properties.put("oauth2:scope", oauth2Provision.getScope()); + } + apiObject.setProperties(properties); + } else if (dataAddress instanceof HttpProxySinkDataAddress) { + apiObject.setProperties(Map.of("type", "HttpProxy")); + } else if (dataAddress instanceof S3DataAddress) { + S3DataAddress a = (S3DataAddress) dataAddress; + apiObject.setProperties( + Map.of( + "type", + "AmazonS3", + "bucketName", + a.getBucketName(), + "region", + a.getRegion(), + "keyName", + a.getKeyName())); + } else if (dataAddress instanceof NullDataAddress) { + // set something that passes validation + apiObject.setProperties(Map.of("type", "HttpData", "baseUrl", "http://localhost")); + } else { + throw new UnsupportedOperationException( + String.format( + "Cannot map data address of type %s to EDC domain", dataAddress.getClass())); + } + + return apiObject; + } + + private ManagementApiAsset mapAsset(Asset asset) { + Map properties = + Map.of( + ManagementApiAsset.ID, asset.getId(), + ManagementApiAsset.DESCRIPTION, asset.getDescription()); + + ManagementApiAsset apiObject = new ManagementApiAsset(); + apiObject.setProperties(properties); + return apiObject; + } + + private Policy mapPolicy(ManagementApiPolicy managementApiPolicy) { + String id = managementApiPolicy.uid; + List permissions = + managementApiPolicy.permissions.stream() + .map(this::mapPermission) + .collect(Collectors.toList()); + + return new Policy(id, permissions); + } - log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); - return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); - } + private ManagementApiPolicy mapPolicy(Policy policy) { + List permissions = + policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); + ManagementApiPolicy managementApiPolicy = new ManagementApiPolicy(); + managementApiPolicy.permissions = permissions; - private T get(String path, Header header, TypeToken typeToken) throws IOException { + return managementApiPolicy; + } + + private ManagementApiPolicyDefinition mapPolicyDefinition(Policy policy) { + ManagementApiPolicyDefinition apiObject = new ManagementApiPolicyDefinition(); + apiObject.id = policy.getId(); + apiObject.policy = mapPolicy(policy); + return apiObject; + } + + private Permission mapPermission(ManagementApiPermission managementApiPermission) { + String target = managementApiPermission.target; + String action = managementApiPermission.action.type; + return new Permission(action, new ArrayList<>(), target); + } + + private ManagementApiPermission mapPermission(Permission permission) { + String target = permission.getTarget(); + String action = permission.getAction(); + + ManagementApiRuleAction apiAction = new ManagementApiRuleAction(); + apiAction.type = action; + + var constraints = + permission.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); + + ManagementApiPermission apiObject = new ManagementApiPermission(); + apiObject.target = target; + apiObject.action = apiAction; + apiObject.constraints = constraints; + return apiObject; + } + + private ManagementConstraint mapConstraint(Constraint constraint) { + if (OrConstraint.class.equals(constraint.getClass())) { + return mapConstraint((OrConstraint) constraint); + } else if (BusinessPartnerNumberConstraint.class.equals(constraint.getClass())) { + return mapConstraint((BusinessPartnerNumberConstraint) constraint); + } else if (PayMeConstraint.class.equals(constraint.getClass())) { + return mapConstraint((PayMeConstraint) constraint); + } else { + throw new UnsupportedOperationException( + "Unsupported constraint type: " + constraint.getClass().getName()); + } + } - final HttpGet get = new HttpGet(path); - get.addHeader(header); - final HttpResponse response = sendRequest(get); - final byte[] json = response.getEntity().getContent().readAllBytes(); + private ManagementAtomicConstraint mapConstraint(PayMeConstraint constraint) { + ManagementApiLiteralExpression leftExpression = new ManagementApiLiteralExpression(); + leftExpression.value = "PayMe"; - log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); - return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); - } + ManagementApiLiteralExpression rightExpression = new ManagementApiLiteralExpression(); + rightExpression.value = String.valueOf(constraint.getAmount()); - private void post(String path, Object object) throws IOException { - post(path, object, new TypeToken() {}); - } + ManagementAtomicConstraint dataManagementApiConstraint = new ManagementAtomicConstraint(); + dataManagementApiConstraint.leftExpression = leftExpression; + dataManagementApiConstraint.rightExpression = rightExpression; + dataManagementApiConstraint.operator = "EQ"; - private T post(String path, Object object, TypeToken typeToken) throws IOException { - final String url = String.format("%s%s", dataMgmtUrl, path); - final HttpPost post = new HttpPost(url); - post.addHeader("Content-Type", "application/json"); - - var json = new Gson().toJson(object); - - log.debug("POST Payload: " + json); - - post.setEntity(new StringEntity(json)); - final CloseableHttpResponse response = sendRequest(post); - - T responseJson = null; - if (!typeToken.equals(new TypeToken() {})) { - final byte[] responseBytes = response.getEntity().getContent().readAllBytes(); - responseJson = - new Gson() - .fromJson(new String(responseBytes, StandardCharsets.UTF_8), typeToken.getType()); - } - - response.close(); - - return responseJson; - } - - private CloseableHttpResponse sendRequest(HttpRequestBase request) throws IOException { - request.addHeader("X-Api-Key", dataMgmtAuthKey); - - log.debug(String.format("Send %-6s %s", request.getMethod(), request.getURI())); - - final CloseableHttpResponse response = httpClient.execute(request); - if (200 > response.getStatusLine().getStatusCode() - || response.getStatusLine().getStatusCode() >= 300) { - throw new RuntimeException( - String.format("Unexpected response: %s", response.getStatusLine())); - } - - return response; - } - - private ContractNegotiation mapNegotiation(ManagementApiNegotiation negotiation) { - - ContractNegotiationState state; - - switch (negotiation.state) { - case "ERROR": - state = ContractNegotiationState.ERROR; - break; - case "INITIAL": - state = ContractNegotiationState.INITIAL; - break; - case "DECLINED": - state = ContractNegotiationState.DECLINED; - break; - case "CONFIRMED": - state = ContractNegotiationState.CONFIRMED; - break; - default: - state = ContractNegotiationState.UNKNOWN; - } - - return new ContractNegotiation(negotiation.id, negotiation.contractAgreementId, state); - } - - private TransferProcess mapTransferProcess(ManagementApiTransferProcess transferProcess) { - - TransferProcessState state; - - switch (transferProcess.state) { - case "COMPLETED": - state = TransferProcessState.COMPLETED; - break; - case "ERROR": - state = TransferProcessState.ERROR; - break; - default: - state = TransferProcessState.UNKNOWN; - } - - return new TransferProcess(transferProcess.id, state); - } - - private ManagementApiDataAddress mapDataAddress(@NonNull DataAddress dataAddress) { - final ManagementApiDataAddress apiObject = new ManagementApiDataAddress(); - - if (dataAddress instanceof HttpProxySourceDataAddress) { - final var address = (HttpProxySourceDataAddress) dataAddress; - var properties = new HashMap(); - properties.put("type", "HttpData"); - properties.put("baseUrl", address.getBaseUrl()); - var oauth2Provision = address.getOauth2Provision(); - if (oauth2Provision != null) { - properties.put("oauth2:tokenUrl", oauth2Provision.getTokenUrl()); - properties.put("oauth2:clientId", oauth2Provision.getClientId()); - properties.put("oauth2:clientSecret", oauth2Provision.getClientSecret()); - properties.put("oauth2:scope", oauth2Provision.getScope()); - } - apiObject.setProperties(properties); - } else if (dataAddress instanceof HttpProxySinkDataAddress) { - apiObject.setProperties(Map.of("type", "HttpProxy")); - } else if (dataAddress instanceof S3DataAddress) { - final S3DataAddress a = (S3DataAddress) dataAddress; - apiObject.setProperties( - Map.of( - "type", - "AmazonS3", - "bucketName", - a.getBucketName(), - "region", - a.getRegion(), - "keyName", - a.getKeyName())); - } else if (dataAddress instanceof NullDataAddress) { - // set something that passes validation - apiObject.setProperties(Map.of("type", "HttpData", "baseUrl", "http://localhost")); - } else { - throw new UnsupportedOperationException( - String.format( - "Cannot map data address of type %s to EDC domain", dataAddress.getClass())); - } - - return apiObject; - } - - private ManagementApiAsset mapAsset(Asset asset) { - final Map properties = - Map.of( - ManagementApiAsset.ID, asset.getId(), - ManagementApiAsset.DESCRIPTION, asset.getDescription()); - - final ManagementApiAsset apiObject = new ManagementApiAsset(); - apiObject.setProperties(properties); - return apiObject; - } - - private Policy mapPolicy(ManagementApiPolicy managementApiPolicy) { - final String id = managementApiPolicy.uid; - final List permissions = - managementApiPolicy.permissions.stream() - .map(this::mapPermission) - .collect(Collectors.toList()); - - return new Policy(id, permissions); - } - - private ManagementApiPolicy mapPolicy(Policy policy) { - final List permissions = - policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); - final ManagementApiPolicy managementApiPolicy = new ManagementApiPolicy(); - managementApiPolicy.permissions = permissions; - - return managementApiPolicy; - } - - private ManagementApiPolicyDefinition mapPolicyDefinition(Policy policy) { - final ManagementApiPolicyDefinition apiObject = new ManagementApiPolicyDefinition(); - apiObject.id = policy.getId(); - apiObject.policy = mapPolicy(policy); - return apiObject; - } - - private Permission mapPermission(ManagementApiPermission managementApiPermission) { - final String target = managementApiPermission.target; - final String action = managementApiPermission.action.type; - return new Permission(action, target, new ArrayList<>()); - } - - private ManagementApiPermission mapPermission(Permission permission) { - final String target = permission.getTarget(); - final String action = permission.getAction(); - - final ManagementApiRuleAction apiAction = new ManagementApiRuleAction(); - apiAction.type = action; - - var constraints = - permission.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); - - final ManagementApiPermission apiObject = new ManagementApiPermission(); - apiObject.target = target; - apiObject.action = apiAction; - apiObject.constraints = constraints; - return apiObject; - } - - private ManagementConstraint mapConstraint(Constraint constraint) { - if (OrConstraint.class.equals(constraint.getClass())) { - return mapConstraint((OrConstraint) constraint); - } else if (BusinessPartnerNumberConstraint.class.equals(constraint.getClass())) { - return mapConstraint((BusinessPartnerNumberConstraint) constraint); - } else if (PayMeConstraint.class.equals(constraint.getClass())) { - return mapConstraint((PayMeConstraint) constraint); - } else { - throw new UnsupportedOperationException( - "Unsupported constraint type: " + constraint.getClass().getName()); - } - } - - private ManagementAtomicConstraint mapConstraint(PayMeConstraint constraint) { - final ManagementApiLiteralExpression leftExpression = new ManagementApiLiteralExpression(); - leftExpression.value = "PayMe"; - - final ManagementApiLiteralExpression rightExpression = new ManagementApiLiteralExpression(); - rightExpression.value = String.valueOf(constraint.getAmount()); - - final ManagementAtomicConstraint dataManagementApiConstraint = new ManagementAtomicConstraint(); - dataManagementApiConstraint.leftExpression = leftExpression; - dataManagementApiConstraint.rightExpression = rightExpression; - dataManagementApiConstraint.operator = "EQ"; - - return dataManagementApiConstraint; - } - - private ManagementAtomicConstraint mapConstraint(BusinessPartnerNumberConstraint constraint) { - final ManagementApiLiteralExpression leftExpression = new ManagementApiLiteralExpression(); - leftExpression.value = "BusinessPartnerNumber"; - - final ManagementApiLiteralExpression rightExpression = new ManagementApiLiteralExpression(); - rightExpression.value = constraint.getBusinessPartnerNumber(); - - final ManagementAtomicConstraint dataManagementApiConstraint = new ManagementAtomicConstraint(); - dataManagementApiConstraint.leftExpression = leftExpression; - dataManagementApiConstraint.rightExpression = rightExpression; - dataManagementApiConstraint.operator = "EQ"; - - return dataManagementApiConstraint; - } - - private ManagementOrConstraint mapConstraint(OrConstraint constraint) { - var orConstraint = new ManagementOrConstraint(); - orConstraint.constraints = - constraint.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); - return orConstraint; - } - - private ContractOffer mapOffer(ManagementApiContractOffer managementApiContractOffer) { - final String id = managementApiContractOffer.id; - final String assetId = - managementApiContractOffer.assetId != null - ? managementApiContractOffer.assetId - : (String) managementApiContractOffer.asset.getProperties().get(ManagementApiAsset.ID); - - final Policy policy = mapPolicy(managementApiContractOffer.getPolicy()); - - return new ContractOffer(id, policy, assetId); - } - - private ManagementApiContractDefinition mapContractDefinition( - ContractDefinition contractDefinition) { - - final ManagementApiContractDefinition apiObject = new ManagementApiContractDefinition(); - apiObject.id = contractDefinition.getId(); - apiObject.accessPolicyId = contractDefinition.getAcccessPolicyId(); - apiObject.contractPolicyId = contractDefinition.getContractPolicyId(); - apiObject.criteria = new ArrayList<>(); - - for (final String assetId : contractDefinition.getAssetIds()) { - ManagementApiCriterion criterion = new ManagementApiCriterion(); - criterion.operandLeft = ManagementApiAsset.ID; - criterion.operator = "="; - criterion.operandRight = assetId; - - apiObject.criteria.add(criterion); - } - - return apiObject; - } - - @Data - private static class ManagementApiNegotiationResponse { - private String id; - } - - @Data - private static class ManagementApiNegotiationPayload { - private String connectorId = "foo"; - private String connectorAddress; - private ManagementApiOffer offer; - } - - @Data - private static class ManagementApiNegotiation { - private String id; - private String state; - private String contractAgreementId; - } - - @Data - private static class ManagementApiTransferProcess { - private String id; - private String state; - } - - @Data - private static class ManagementApiOffer { - private String offerId; - private String assetId; - private ManagementApiPolicy policy; - } - - @Data - private static class ManagementApiTransfer { - private String connectorId = "foo"; - private String connectorAddress; - private String contractId; - private String assetId; - private String protocol; - private ManagementApiDataAddress dataDestination; - private boolean managedResources; - private ManagementApiTransferType transferType; - private ManagementApiProperties properties; - } - - @Data - private static class ManagementApiTransferType { - private String contentType = "application/octet-stream"; - private boolean isFinite = true; - } - - @Data - private static class ManagementApiTransferResponse { - private String id; - } - - @Data - private static class ManagementApiAssetCreate { - private ManagementApiAsset asset; - private ManagementApiDataAddress dataAddress; - } - - @Data - private static class ManagementApiAsset { - public static final String ID = "asset:prop:id"; - public static final String DESCRIPTION = "asset:prop:description"; - - private Map properties; - } - - @Data - private static class ManagementApiDataAddress { - public static final String TYPE = "type"; - private Map properties; - } - - @Data - private static class ManagementApiProperties { - @SerializedName(value = "receiver.http.endpoint") - private final String receiverHttpEndpoint; - } - - @Data - private static class ManagementApiPolicyDefinition { - private String id; - private ManagementApiPolicy policy; - } - - @Data - private static class ManagementApiPolicy { - private String uid; - private List permissions = new ArrayList<>(); - } - - @Data - private static class ManagementApiPermission { - private String edctype = "dataspaceconnector:permission"; - private ManagementApiRuleAction action; - private String target; - private List constraints = new ArrayList<>(); - } - - @Data - private static class ManagementAtomicConstraint implements ManagementConstraint { - private String edctype = "AtomicConstraint"; - private ManagementApiLiteralExpression leftExpression; - private ManagementApiLiteralExpression rightExpression; - private String operator; - } - - @Data - private static class ManagementOrConstraint implements ManagementConstraint { - private String edctype = "dataspaceconnector:orconstraint"; - private List constraints; - } - - private interface ManagementConstraint {} - - @Data - private static class ManagementApiLiteralExpression { - private String edctype = "dataspaceconnector:literalexpression"; - private String value; - } - - @Data - private static class ManagementApiRuleAction { - private String type; - } - - @Data - private static class ManagementApiContractDefinition { - private String id; - private String accessPolicyId; - private String contractPolicyId; - private List criteria = new ArrayList<>(); - } - - @Data - private static class ManagementApiCriterion { - private Object operandLeft; - private String operator; - private Object operandRight; - } - - @Data - private static class ManagementApiContractOffer { - private String id; - private ManagementApiPolicy policy; - private ManagementApiAsset asset; - private String assetId; - } - - @Data - private static class ManagementApiContractOfferCatalog { - private String id; - private List contractOffers = new ArrayList<>(); - } + return dataManagementApiConstraint; + } + + private ManagementAtomicConstraint mapConstraint(BusinessPartnerNumberConstraint constraint) { + ManagementApiLiteralExpression leftExpression = new ManagementApiLiteralExpression(); + leftExpression.value = "BusinessPartnerNumber"; + + ManagementApiLiteralExpression rightExpression = new ManagementApiLiteralExpression(); + rightExpression.value = constraint.getBusinessPartnerNumber(); + + ManagementAtomicConstraint dataManagementApiConstraint = new ManagementAtomicConstraint(); + dataManagementApiConstraint.leftExpression = leftExpression; + dataManagementApiConstraint.rightExpression = rightExpression; + dataManagementApiConstraint.operator = "EQ"; + + return dataManagementApiConstraint; + } + + private ManagementOrConstraint mapConstraint(OrConstraint constraint) { + var orConstraint = new ManagementOrConstraint(); + orConstraint.constraints = + constraint.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); + return orConstraint; + } + + private ContractOffer mapOffer(ManagementApiContractOffer managementApiContractOffer) { + String id = managementApiContractOffer.id; + String assetId = + managementApiContractOffer.assetId != null + ? managementApiContractOffer.assetId + : (String) managementApiContractOffer.asset.getProperties().get(ManagementApiAsset.ID); + + Policy policy = mapPolicy(managementApiContractOffer.getPolicy()); + + return new ContractOffer(id, policy, assetId); + } + + private ManagementApiContractDefinition mapContractDefinition( + ContractDefinition contractDefinition) { + + ManagementApiContractDefinition apiObject = new ManagementApiContractDefinition(); + apiObject.id = contractDefinition.getId(); + apiObject.accessPolicyId = contractDefinition.getAcccessPolicyId(); + apiObject.contractPolicyId = contractDefinition.getContractPolicyId(); + apiObject.criteria = new ArrayList<>(); + + for (String assetId : contractDefinition.getAssetIds()) { + ManagementApiCriterion criterion = new ManagementApiCriterion(); + criterion.operandLeft = ManagementApiAsset.ID; + criterion.operator = "="; + criterion.operandRight = assetId; + + apiObject.criteria.add(criterion); + } + + return apiObject; + } + + private interface ManagementConstraint { + } + + + private static class ManagementApiNegotiationResponse { + private String id; + + + public String getId() { + return id; + } + } + + + private static class ManagementApiNegotiationPayload { + private final String connectorId = "foo"; + private String connectorAddress; + private ManagementApiOffer offer; + } + + private static class ManagementApiNegotiation { + private String id; + private String state; + private String contractAgreementId; + } + + private static class ManagementApiTransferProcess { + private String id; + private String state; + } + + + private static class ManagementApiOffer { + private String offerId; + private String assetId; + private ManagementApiPolicy policy; + } + + + private static class ManagementApiTransfer { + private final String connectorId = "foo"; + private String connectorAddress; + private String contractId; + private String assetId; + private String protocol; + private ManagementApiDataAddress dataDestination; + private boolean managedResources; + private ManagementApiTransferType transferType; + private ManagementApiProperties properties; + } + + + private static class ManagementApiTransferType { + private final String contentType = "application/octet-stream"; + private final boolean isFinite = true; + } + + + private static class ManagementApiTransferResponse { + private String id; + + + public String getId() { + return id; + } + } + + private static class ManagementApiAssetCreate { + private ManagementApiAsset asset; + private ManagementApiDataAddress dataAddress; + } + + private static class ManagementApiAsset { + public static final String ID = "asset:prop:id"; + public static final String DESCRIPTION = "asset:prop:description"; + + private Map properties; + + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + + private static class ManagementApiDataAddress { + public static final String TYPE = "type"; + private Map properties; + + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + + private static class ManagementApiProperties { + @SerializedName(value = "receiver.http.endpoint") + private final String receiverHttpEndpoint; + + private ManagementApiProperties(String receiverHttpEndpoint) { + this.receiverHttpEndpoint = receiverHttpEndpoint; + } + } + + + private static class ManagementApiPolicyDefinition { + private String id; + private ManagementApiPolicy policy; + } + + + private static class ManagementApiPolicy { + private String uid; + private List permissions = new ArrayList<>(); + } + + + private static class ManagementApiPermission { + private final String edctype = "dataspaceconnector:permission"; + private ManagementApiRuleAction action; + private String target; + private List constraints = new ArrayList<>(); + } + + + private static class ManagementAtomicConstraint implements ManagementConstraint { + private final String edctype = "AtomicConstraint"; + private ManagementApiLiteralExpression leftExpression; + private ManagementApiLiteralExpression rightExpression; + private String operator; + } + + + private static class ManagementOrConstraint implements ManagementConstraint { + private final String edctype = "dataspaceconnector:orconstraint"; + private List constraints; + } + + + private static class ManagementApiLiteralExpression { + private final String edctype = "dataspaceconnector:literalexpression"; + private String value; + } + + + private static class ManagementApiRuleAction { + private String type; + } + + + private static class ManagementApiContractDefinition { + private String id; + private String accessPolicyId; + private String contractPolicyId; + private List criteria = new ArrayList<>(); + } + + + private static class ManagementApiCriterion { + private Object operandLeft; + private String operator; + private Object operandRight; + } + + + private static class ManagementApiContractOffer { + private String id; + private ManagementApiPolicy policy; + private ManagementApiAsset asset; + private String assetId; + + + public ManagementApiPolicy getPolicy() { + return policy; + } + } + + + private static class ManagementApiContractOfferCatalog { + private final List contractOffers = new ArrayList<>(); + private String id; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java index d1a199fd1..49a2353d1 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java @@ -20,6 +20,9 @@ package org.eclipse.tractusx.edc.tests; +import java.util.Locale; +import java.util.Objects; + import static org.eclipse.tractusx.edc.tests.Constants.AWS_ACCESS_KEY_ID; import static org.eclipse.tractusx.edc.tests.Constants.AWS_SECRET_ACCESS_KEY; import static org.eclipse.tractusx.edc.tests.Constants.BACKEND_SERVICE_BACKEND_API_URL; @@ -32,45 +35,165 @@ import static org.eclipse.tractusx.edc.tests.Constants.EDC_AWS_ENDPOINT_OVERRIDE; import static org.eclipse.tractusx.edc.tests.Constants.IDS_URL; -import java.util.Locale; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; - -@Builder(access = AccessLevel.PRIVATE) -@Getter -@ToString public class Environment { - @NonNull private final String dataManagementAuthKey; - @NonNull private final String dataManagementUrl; - @NonNull private final String idsUrl; - @NonNull private final String dataPlaneUrl; - @NonNull private final String backendServiceBackendApiUrl; - @NonNull private final String databaseUrl; - @NonNull private final String databaseUser; - @NonNull private final String databasePassword; - @NonNull private final String awsEndpointOverride; - @NonNull private final String awsAccessKey; - @NonNull private final String awsSecretAccessKey; - - public static Environment byName(String name) { - name = name.toUpperCase(Locale.ROOT); - - return Environment.builder() - .dataManagementUrl(System.getenv(String.join("_", name, DATA_MANAGEMENT_URL))) - .dataManagementAuthKey(System.getenv(String.join("_", name, DATA_MANAGEMENT_API_AUTH_KEY))) - .idsUrl(System.getenv(String.join("_", name, IDS_URL))) - .dataPlaneUrl(System.getenv(String.join("_", name, DATA_PLANE_URL))) - .backendServiceBackendApiUrl( - System.getenv(String.join("_", name, BACKEND_SERVICE_BACKEND_API_URL))) - .databaseUrl(System.getenv(String.join("_", name, DATABASE_URL))) - .databaseUser(System.getenv(String.join("_", name, DATABASE_USER))) - .databasePassword(System.getenv(String.join("_", name, DATABASE_PASSWORD))) - .awsEndpointOverride(System.getenv(EDC_AWS_ENDPOINT_OVERRIDE)) - .awsAccessKey(System.getenv(String.join("_", name, AWS_ACCESS_KEY_ID))) - .awsSecretAccessKey(System.getenv(String.join("_", name, AWS_SECRET_ACCESS_KEY))) - .build(); - } + + private String awsEndpointOverride; + private String awsAccessKey; + private String awsSecretAccessKey; + private String dataManagementAuthKey; + private String dataManagementUrl; + private String idsUrl; + private String dataPlaneUrl; + private String backendServiceBackendApiUrl; + private String databaseUrl; + private String databaseUser; + private String databasePassword; + + private Environment() { + + } + + + public static Environment byName(String name) { + var upperName = name.toUpperCase(Locale.ROOT); + + return Environment.Builder.newInstance() + .dataManagementUrl(System.getenv(String.join("_", upperName, DATA_MANAGEMENT_URL))) + .dataManagementAuthKey(System.getenv(String.join("_", upperName, DATA_MANAGEMENT_API_AUTH_KEY))) + .idsUrl(System.getenv(String.join("_", upperName, IDS_URL))) + .dataPlaneUrl(System.getenv(String.join("_", upperName, DATA_PLANE_URL))) + .backendServiceBackendApiUrl( + System.getenv(String.join("_", upperName, BACKEND_SERVICE_BACKEND_API_URL))) + .databaseUrl(System.getenv(String.join("_", upperName, DATABASE_URL))) + .databaseUser(System.getenv(String.join("_", upperName, DATABASE_USER))) + .databasePassword(System.getenv(String.join("_", upperName, DATABASE_PASSWORD))) + .awsEndpointOverride(System.getenv(EDC_AWS_ENDPOINT_OVERRIDE)) + .awsAccessKey(System.getenv(String.join("_", upperName, AWS_ACCESS_KEY_ID))) + .awsSecretAccessKey(System.getenv(String.join("_", upperName, AWS_SECRET_ACCESS_KEY))) + .build(); + } + + public String getIdsUrl() { + return idsUrl; + } + + public String getAwsEndpointOverride() { + return awsEndpointOverride; + } + + public String getAwsSecretAccessKey() { + return awsSecretAccessKey; + } + + public String getAwsAccessKey() { + return awsAccessKey; + } + + public String getBackendServiceBackendApiUrl() { + return backendServiceBackendApiUrl; + } + + public String getDatabasePassword() { + return databasePassword; + } + + public String getDatabaseUrl() { + return databaseUrl; + } + + public String getDatabaseUser() { + return databaseUser; + } + + public String getDataManagementAuthKey() { + return dataManagementAuthKey; + } + + public String getDataManagementUrl() { + return dataManagementUrl; + } + + private static class Builder { + + + private final Environment environment; + + private Builder() { + environment = new Environment(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder awsEndpointOverride(String val) { + environment.awsEndpointOverride = val; + return this; + } + + public Builder awsAccessKey(String val) { + environment.awsAccessKey = val; + return this; + } + + public Builder awsSecretAccessKey(String val) { + environment.awsSecretAccessKey = val; + return this; + } + + public Builder dataManagementAuthKey(String val) { + environment.dataManagementAuthKey = val; + return this; + } + + public Builder dataManagementUrl(String val) { + environment.dataManagementUrl = val; + return this; + } + + public Builder idsUrl(String val) { + environment.idsUrl = val; + return this; + } + + public Builder dataPlaneUrl(String val) { + environment.dataPlaneUrl = val; + return this; + } + + public Builder backendServiceBackendApiUrl(String val) { + environment.backendServiceBackendApiUrl = val; + return this; + } + + public Builder databaseUrl(String val) { + environment.databaseUrl = val; + return this; + } + + public Builder databaseUser(String val) { + environment.databaseUser = val; + return this; + } + + public Builder databasePassword(String val) { + environment.databasePassword = val; + return this; + } + + public Environment build() { + Objects.requireNonNull(environment.awsAccessKey); + Objects.requireNonNull(environment.awsEndpointOverride); + Objects.requireNonNull(environment.awsSecretAccessKey); + Objects.requireNonNull(environment.backendServiceBackendApiUrl); + Objects.requireNonNull(environment.databaseUrl); + Objects.requireNonNull(environment.databasePassword); + Objects.requireNonNull(environment.databaseUser); + Objects.requireNonNull(environment.dataManagementUrl); + Objects.requireNonNull(environment.dataPlaneUrl); + Objects.requireNonNull(environment.dataManagementAuthKey); + Objects.requireNonNull(environment.idsUrl); + return environment; + } + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java index 24f68e3a1..39a743ab5 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java @@ -4,12 +4,13 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; -import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.edc.tests.data.Asset; import org.eclipse.tractusx.edc.tests.data.DataAddress; import org.eclipse.tractusx.edc.tests.data.HttpProxySinkDataAddress; import org.eclipse.tractusx.edc.tests.data.HttpProxySourceDataAddress; import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.time.Duration; @@ -20,9 +21,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -@Slf4j public class HttpProxyTransferSteps { + private static final Logger log = LoggerFactory.getLogger(HttpProxyTransferSteps.class); + private static final String ID = "id"; private static final String DESCRIPTION = "description"; private static final String BASE_URL = "baseUrl"; diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java index 5872d2dfe..7a713ff1b 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java @@ -23,11 +23,6 @@ import io.cucumber.datatable.DataTable; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.edc.tests.data.ContractNegotiation; import org.eclipse.tractusx.edc.tests.data.ContractNegotiationState; import org.eclipse.tractusx.edc.tests.data.Negotiation; @@ -35,60 +30,66 @@ import org.eclipse.tractusx.edc.tests.data.Policy; import org.junit.jupiter.api.Assertions; -@Slf4j +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + public class NegotiationSteps { - private static final String DEFINITION_ID = "definition id"; - private static final String ASSET_ID = "asset id"; - private ContractNegotiation lastInitiatedNegotiation; + private static final String DEFINITION_ID = "definition id"; + private static final String ASSET_ID = "asset id"; - @When("'{connector}' sends '{connector}' an offer without constraints") - public void sendAnOfferWithoutConstraints(Connector sender, Connector receiver, DataTable table) - throws IOException { + private ContractNegotiation lastInitiatedNegotiation; - final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); - final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + @When("'{connector}' sends '{connector}' an offer without constraints") + public void sendAnOfferWithoutConstraints(Connector sender, Connector receiver, DataTable table) + throws IOException { - for (Map map : table.asMaps()) { - final String definitionId = map.get(DEFINITION_ID); - final String assetId = map.get(ASSET_ID); + DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - final Permission permission = new Permission("USE", null, new ArrayList<>()); - final Policy policy = new Policy("foo", List.of(permission)); + for (Map map : table.asMaps()) { + String definitionId = map.get(DEFINITION_ID); + String assetId = map.get(ASSET_ID); - final Negotiation negotiation = - dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); + Permission permission = new Permission("USE", new ArrayList<>(), null); + Policy policy = new Policy("foo", List.of(permission)); - // wait for negotiation to complete - negotiation.waitUntilComplete(dataManagementAPI); + Negotiation negotiation = + dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); - lastInitiatedNegotiation = dataManagementAPI.getNegotiation(negotiation.getId()); + // wait for negotiation to complete + negotiation.waitUntilComplete(dataManagementAPI); + + lastInitiatedNegotiation = dataManagementAPI.getNegotiation(negotiation.getId()); + } } - } - @When("'{connector}' successfully negotiation a contract agreement with '{connector}'") - public void sokratesSuccessfullyNegotiationAContractAgreementPlatoFor( - Connector consumer, Connector provider, DataTable table) throws IOException { - final DataManagementAPI api = consumer.getDataManagementAPI(); + @When("'{connector}' successfully negotiation a contract agreement with '{connector}'") + public void sokratesSuccessfullyNegotiationAContractAgreementPlatoFor( + Connector consumer, Connector provider, DataTable table) throws IOException { + DataManagementAPI api = consumer.getDataManagementAPI(); - final Map map = table.asMap(); - final String definitionId = map.get(DEFINITION_ID); - final String assetId = map.get(ASSET_ID); + Map map = table.asMap(); + String definitionId = map.get(DEFINITION_ID); + String assetId = map.get(ASSET_ID); - // as default always the "allow all" policy is used. So we can assume this here, too. - final Permission permission = new Permission("USE", null, new ArrayList<>()); - final Policy policy = new Policy("policy-id", List.of(permission)); + // as default always the "allow all" policy is used. So we can assume this here, too. + Permission permission = new Permission("USE", new ArrayList<>(), null); + Policy policy = new Policy("policy-id", List.of(permission)); - final String receiverUrl = provider.getEnvironment().getIdsUrl(); - final Negotiation negotiation = - api.initiateNegotiation(receiverUrl, assetId, definitionId, policy); + String receiverUrl = provider.getEnvironment().getIdsUrl(); + Negotiation negotiation = + api.initiateNegotiation(receiverUrl, assetId, definitionId, policy); - negotiation.waitUntilComplete(api); - } + negotiation.waitUntilComplete(api); + } - @Then("the negotiation is declined") - public void assertLastNegotiationDeclined() { - Assertions.assertEquals(ContractNegotiationState.DECLINED, lastInitiatedNegotiation.getState()); - } + @Then("the negotiation is declined") + public void assertLastNegotiationDeclined() { + Assertions.assertEquals(ContractNegotiationState.DECLINED, lastInitiatedNegotiation.getState()); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java index a7ede22be..d8bac4466 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java @@ -20,45 +20,54 @@ package org.eclipse.tractusx.edc.tests; -import static java.util.Arrays.stream; -import static java.util.stream.Collectors.toList; - import io.cucumber.datatable.DataTable; import io.cucumber.java.en.Given; +import org.eclipse.tractusx.edc.tests.data.BusinessPartnerNumberConstraint; +import org.eclipse.tractusx.edc.tests.data.Constraint; +import org.eclipse.tractusx.edc.tests.data.OrConstraint; +import org.eclipse.tractusx.edc.tests.data.PayMeConstraint; +import org.eclipse.tractusx.edc.tests.data.Permission; +import org.eclipse.tractusx.edc.tests.data.Policy; + import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.eclipse.tractusx.edc.tests.data.*; + +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toList; public class PolicyStepDefs { - @Given("'{connector}' has the following policies") - public void hasPolicies(Connector connector, DataTable table) throws Exception { - var api = connector.getDataManagementAPI(); - var policies = table.asMaps().stream().map(this::parseRow).collect(toList()); + @Given("'{connector}' has the following policies") + public void hasPolicies(Connector connector, DataTable table) throws Exception { + var api = connector.getDataManagementAPI(); + var policies = table.asMaps().stream().map(this::parseRow).collect(toList()); - for (var policy : policies) api.createPolicy(policy); - } + for (var policy : policies) { + api.createPolicy(policy); + } + } - private Policy parseRow(Map row) { - var id = row.get("id"); - var action = row.get("action"); - var constraints = new ArrayList(); + private Policy parseRow(Map row) { + var id = row.get("id"); + var action = row.get("action"); + var constraints = new ArrayList(); - var businessPartnerNumber = row.get("businessPartnerNumber"); - if (businessPartnerNumber != null && !businessPartnerNumber.isBlank()) { - var bpnConstraints = - stream(businessPartnerNumber.split(",")) - .map(BusinessPartnerNumberConstraint::new) - .collect(toList()); - constraints.add(new OrConstraint(bpnConstraints)); - } + var businessPartnerNumber = row.get("businessPartnerNumber"); + if (businessPartnerNumber != null && !businessPartnerNumber.isBlank()) { + var bpnConstraints = + stream(businessPartnerNumber.split(",")) + .map(BusinessPartnerNumberConstraint::new) + .collect(toList()); + constraints.add(new OrConstraint(bpnConstraints)); + } - var payMe = row.get("payMe"); - if (payMe != null && !payMe.isBlank()) - constraints.add(new PayMeConstraint(Double.parseDouble(payMe))); + var payMe = row.get("payMe"); + if (payMe != null && !payMe.isBlank()) { + constraints.add(new PayMeConstraint(Double.parseDouble(payMe))); + } - var permission = new Permission(action, null, constraints); - return new Policy(id, List.of(permission)); - } + var permission = new Permission(action, constraints, null); + return new Policy(id, List.of(permission)); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java index c4bc85a27..05f5f1242 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java @@ -19,21 +19,10 @@ package org.eclipse.tractusx.edc.tests; -import static org.awaitility.Awaitility.await; -import static org.junit.jupiter.api.Assertions.fail; - import io.cucumber.datatable.DataTable; import io.cucumber.java.AfterAll; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; import org.eclipse.tractusx.edc.tests.data.Asset; import org.eclipse.tractusx.edc.tests.data.DataAddress; import org.eclipse.tractusx.edc.tests.data.Negotiation; @@ -45,135 +34,145 @@ import org.eclipse.tractusx.edc.tests.util.Timeouts; import org.junit.jupiter.api.Assertions; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.fail; + public class S3FileTransferStepsDefs { - @Given("'{connector}' has an empty storage bucket called {string}") - public void hasEmptyStorageBucket(Connector connector, String bucketName) { - S3Client s3 = connector.getS3Client(); + private static final String COMPLETION_MARKER = ".complete"; + private File fileToTransfer; + private String assetId; + private String agreementId; - s3.createBucket(bucketName); + @AfterAll + public static void bucketsCleanup() { + S3Client s3 = new S3Client(Environment.byName("Sokrates")); + s3.deleteAllBuckets(); + } - Assertions.assertTrue(s3.listBuckets().contains(bucketName)); - Assertions.assertEquals(0, s3.listBucketContent(bucketName).size()); - } + @Given("'{connector}' has an empty storage bucket called {string}") + public void hasEmptyStorageBucket(Connector connector, String bucketName) { + S3Client s3 = connector.getS3Client(); - private File fileToTransfer; + s3.createBucket(bucketName); - @Given("'{connector}' has a storage bucket called {string} with the file called {string}") - public void hasAStorageBucketWithTheFile(Connector connector, String bucketName, String fileName) - throws IOException { + Assertions.assertTrue(s3.listBuckets().contains(bucketName)); + Assertions.assertEquals(0, s3.listBucketContent(bucketName).size()); + } - S3Client s3 = connector.getS3Client(); - s3.createBucket(bucketName); - fileToTransfer = s3.uploadFile(bucketName, fileName); + @Given("'{connector}' has a storage bucket called {string} with the file called {string}") + public void hasAStorageBucketWithTheFile(Connector connector, String bucketName, String fileName) + throws IOException { - Set bucketContent = s3.listBucketContent(bucketName); + S3Client s3 = connector.getS3Client(); + s3.createBucket(bucketName); + fileToTransfer = s3.uploadFile(bucketName, fileName); - Assertions.assertEquals(1, bucketContent.size()); - Assertions.assertTrue(bucketContent.contains(fileName)); - } + Set bucketContent = s3.listBucketContent(bucketName); - @Given("'{connector}' has the following S3 assets") - public void hasAssets(Connector connector, DataTable table) { - final DataManagementAPI api = connector.getDataManagementAPI(); + Assertions.assertEquals(1, bucketContent.size()); + Assertions.assertTrue(bucketContent.contains(fileName)); + } - parseDataTable(table) - .forEach( - asset -> { - try { - api.createAsset(asset); - } catch (IOException e) { - fail(e.getMessage()); - } - }); - } + @Given("'{connector}' has the following S3 assets") + public void hasAssets(Connector connector, DataTable table) { + DataManagementAPI api = connector.getDataManagementAPI(); + + parseDataTable(table) + .forEach( + asset -> { + try { + api.createAsset(asset); + } catch (IOException e) { + fail(e.getMessage()); + } + }); + } - private String assetId; - private String agreementId; + @Then("'{connector}' negotiates the contract successfully with '{connector}'") + public void negotiateContract(Connector sender, Connector receiver, DataTable dataTable) + throws IOException { - @Then("'{connector}' negotiates the contract successfully with '{connector}'") - public void negotiateContract(Connector sender, Connector receiver, DataTable dataTable) - throws IOException { + String definitionId = dataTable.asMaps().get(0).get("contract offer id"); + assetId = dataTable.asMaps().get(0).get("asset id"); + String policyId = dataTable.asMaps().get(0).get("policy id"); - String definitionId = dataTable.asMaps().get(0).get("contract offer id"); - assetId = dataTable.asMaps().get(0).get("asset id"); - String policyId = dataTable.asMaps().get(0).get("policy id"); + Policy policy = + new Policy(policyId, List.of(new Permission("USE", new ArrayList<>(), null))); - final Policy policy = - new Policy(policyId, List.of(new Permission("USE", null, new ArrayList<>()))); + DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); - final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + Negotiation negotiation = + dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); + negotiation.waitUntilComplete(dataManagementAPI); - final Negotiation negotiation = - dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); - negotiation.waitUntilComplete(dataManagementAPI); + agreementId = dataManagementAPI.getNegotiation(negotiation.getId()).getAgreementId(); + } - agreementId = dataManagementAPI.getNegotiation(negotiation.getId()).getAgreementId(); - } + @Then("'{connector}' initiate S3 transfer process from '{connector}'") + public void initiateTransferProcess(Connector sender, Connector receiver, DataTable dataTable) + throws IOException { + DataAddress dataAddress = createDataAddress(dataTable.asMaps().get(0)); - @Then("'{connector}' initiate S3 transfer process from '{connector}'") - public void initiateTransferProcess(Connector sender, Connector receiver, DataTable dataTable) - throws IOException { - DataAddress dataAddress = createDataAddress(dataTable.asMaps().get(0)); + DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); - final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + Transfer transferProcess = + dataManagementAPI.initiateTransferProcess( + receiverIdsUrl, agreementId, assetId, dataAddress); + transferProcess.waitUntilComplete(dataManagementAPI); - final Transfer transferProcess = - dataManagementAPI.initiateTransferProcess( - receiverIdsUrl, agreementId, assetId, dataAddress); - transferProcess.waitUntilComplete(dataManagementAPI); + Assertions.assertNotNull(transferProcess.getId()); + } - Assertions.assertNotNull(transferProcess.getId()); - } + @Then("'{connector}' has a storage bucket called {string} with transferred file called {string}") + public void consumerHasAStorageBucketWithFileTransferred( + Connector connector, String bucketName, String fileName) throws IOException { + S3Client s3 = connector.getS3Client(); + await() + .pollDelay(Duration.ofMillis(500)) + .atMost(Timeouts.FILE_TRANSFER) + .until(() -> isFilePresent(s3, bucketName, fileName + COMPLETION_MARKER)); + + Set bucketContent = s3.listBucketContent(bucketName); + + Assertions.assertEquals(2, bucketContent.size()); + Assertions.assertTrue(bucketContent.contains(fileName)); + Assertions.assertArrayEquals( + Files.readAllBytes(fileToTransfer.toPath()), + Files.readAllBytes(s3.downloadFile(bucketName, fileName).toPath())); + } - private static final String COMPLETION_MARKER = ".complete"; + private boolean isFilePresent(S3Client s3, String bucketName, String fileName) { + return s3.listBucketContent(bucketName).contains(fileName); + } - @Then("'{connector}' has a storage bucket called {string} with transferred file called {string}") - public void consumerHasAStorageBucketWithFileTransferred( - Connector connector, String bucketName, String fileName) throws IOException { - S3Client s3 = connector.getS3Client(); - await() - .pollDelay(Duration.ofMillis(500)) - .atMost(Timeouts.FILE_TRANSFER) - .until(() -> isFilePresent(s3, bucketName, fileName + COMPLETION_MARKER)); + private List parseDataTable(DataTable table) { + List assetsWithDataAddresses = new ArrayList<>(); - Set bucketContent = s3.listBucketContent(bucketName); + for (Map map : table.asMaps()) { + String id = map.get("id"); + String description = map.get("description"); + assetsWithDataAddresses.add(new Asset(id, description, createDataAddress(map))); + } - Assertions.assertEquals(2, bucketContent.size()); - Assertions.assertTrue(bucketContent.contains(fileName)); - Assertions.assertArrayEquals( - Files.readAllBytes(fileToTransfer.toPath()), - Files.readAllBytes(s3.downloadFile(bucketName, fileName).toPath())); - } - - private boolean isFilePresent(S3Client s3, String bucketName, String fileName) { - return s3.listBucketContent(bucketName).contains(fileName); - } - - private List parseDataTable(DataTable table) { - final List assetsWithDataAddresses = new ArrayList<>(); - - for (Map map : table.asMaps()) { - String id = map.get("id"); - String description = map.get("description"); - assetsWithDataAddresses.add(new Asset(id, description, createDataAddress(map))); + return assetsWithDataAddresses; } - return assetsWithDataAddresses; - } - - private DataAddress createDataAddress(Map map) { - final String bucketName = map.get("data_address_s3_bucket_name"); - final String region = map.get("data_address_s3_region"); - final String keyName = map.get("data_address_s3_key_name"); - return new S3DataAddress(bucketName, region, keyName); - } - - @AfterAll - public static void bucketsCleanup() { - S3Client s3 = new S3Client(Environment.byName("Sokrates")); - s3.deleteAllBuckets(); - } + private DataAddress createDataAddress(Map map) { + String bucketName = map.get("data_address_s3_bucket_name"); + String region = map.get("data_address_s3_region"); + String keyName = map.get("data_address_s3_key_name"); + return new S3DataAddress(bucketName, region, keyName); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java index acccef8d8..47142d0e9 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java @@ -19,14 +19,28 @@ */ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class Asset { - @NonNull String Id; + private final String id; + private final String description; + private final DataAddress dataAddress; - @NonNull String description; + public Asset(String id, String description, DataAddress dataAddress) { + this.id = Objects.requireNonNull(id); + this.description = Objects.requireNonNull(description); + this.dataAddress = Objects.requireNonNull(dataAddress); + } - @NonNull DataAddress dataAddress; + public String getId() { + return id; + } + + public String getDescription() { + return description; + } + + public DataAddress getDataAddress() { + return dataAddress; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java index b9c64d158..f276b3d34 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java @@ -1,10 +1,16 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class BusinessPartnerNumberConstraint implements Constraint { - @NonNull String businessPartnerNumber; + private final String businessPartnerNumber; + + public BusinessPartnerNumberConstraint(String businessPartnerNumber) { + this.businessPartnerNumber = Objects.requireNonNull(businessPartnerNumber); + } + + public String getBusinessPartnerNumber() { + return businessPartnerNumber; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java index a9fca04a1..c90fe1788 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java @@ -20,17 +20,42 @@ package org.eclipse.tractusx.edc.tests.data; import java.util.List; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class ContractDefinition { - @NonNull String id; + private final String id; + + private final String contractPolicyId; + private final String acccessPolicyId; + + private final List assetIds; + private final Long validity; + + public ContractDefinition(String id, String contractPolicyId, String acccessPolicyId, List assetIds, Long validity) { + this.id = Objects.requireNonNull(id); + this.contractPolicyId = Objects.requireNonNull(contractPolicyId); + this.acccessPolicyId = Objects.requireNonNull(acccessPolicyId); + this.assetIds = assetIds; + this.validity = validity; + } + + public String getId() { + return id; + } + + public String getContractPolicyId() { + return contractPolicyId; + } + + public String getAcccessPolicyId() { + return acccessPolicyId; + } + + public List getAssetIds() { + return assetIds; + } - @NonNull String contractPolicyId; - @NonNull String acccessPolicyId; - List assetIds; - Long validity; } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java index 109249744..67f9dafb0 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java @@ -20,12 +20,29 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class ContractNegotiation { - @NonNull String id; - String agreementId; - @NonNull ContractNegotiationState state; + private final String id; + private final ContractNegotiationState state; + private final String agreementId; + + + public ContractNegotiation(String id, ContractNegotiationState state, String agreementId) { + this.id = Objects.requireNonNull(id); + this.state = Objects.requireNonNull(state); + this.agreementId = agreementId; + } + + public String getId() { + return id; + } + + public ContractNegotiationState getState() { + return state; + } + + public String getAgreementId() { + return agreementId; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java index 75dfd8d27..7ac87cb9a 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java @@ -19,12 +19,28 @@ */ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class ContractOffer { - @NonNull String id; - Policy policy; - String assetId; + private final String id; + private final Policy policy; + private final String assetId; + + public ContractOffer(String id, Policy policy, String assetId) { + this.id = Objects.requireNonNull(id); + this.policy = policy; + this.assetId = assetId; + } + + public String getId() { + return id; + } + + public Policy getPolicy() { + return policy; + } + + public String getAssetId() { + return assetId; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/HttpProxySourceDataAddress.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/HttpProxySourceDataAddress.java index 4a5946cc9..97f300611 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/HttpProxySourceDataAddress.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/HttpProxySourceDataAddress.java @@ -1,18 +1,52 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class HttpProxySourceDataAddress implements DataAddress { - @NonNull String baseUrl; - Oauth2Provision oauth2Provision; - - @Value - public static class Oauth2Provision { - @NonNull String tokenUrl; - @NonNull String clientId; - @NonNull String clientSecret; - String scope; - } + private final String baseUrl; + private final Oauth2Provision oauth2Provision; + + public HttpProxySourceDataAddress(String baseUrl, Oauth2Provision oauth2Provision) { + this.baseUrl = Objects.requireNonNull(baseUrl); + this.oauth2Provision = oauth2Provision; + } + + public String getBaseUrl() { + return baseUrl; + } + + public Oauth2Provision getOauth2Provision() { + return oauth2Provision; + } + + public static class Oauth2Provision { + private final String tokenUrl; + private final String clientId; + private final String clientSecret; + private final String scope; + + public Oauth2Provision(String tokenUrl, String clientId, String clientSecret, String scope) { + this.tokenUrl = Objects.requireNonNull(tokenUrl); + this.clientId = Objects.requireNonNull(clientId); + this.clientSecret = Objects.requireNonNull(clientSecret); + this.scope = scope; + } + + public String getTokenUrl() { + return tokenUrl; + } + + public String getScope() { + return scope; + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java index 40845e4c0..ddc715c9b 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java @@ -1,34 +1,43 @@ package org.eclipse.tractusx.edc.tests.data; -import static org.awaitility.Awaitility.await; +import org.eclipse.tractusx.edc.tests.DataManagementAPI; +import org.eclipse.tractusx.edc.tests.util.Timeouts; -import groovyjarjarantlr4.v4.runtime.misc.NotNull; import java.io.IOException; import java.time.Duration; +import java.util.Objects; import java.util.stream.Stream; -import lombok.Value; -import org.eclipse.tractusx.edc.tests.DataManagementAPI; -import org.eclipse.tractusx.edc.tests.util.Timeouts; -@Value +import static org.awaitility.Awaitility.await; + + public class Negotiation { - @NotNull String id; - - public void waitUntilComplete(DataManagementAPI dataManagementAPI) { - await() - .pollDelay(Duration.ofMillis(2000)) - .atMost(Timeouts.CONTRACT_NEGOTIATION) - .until(() -> isComplete(dataManagementAPI)); - } - - public boolean isComplete(DataManagementAPI dataManagementAPI) throws IOException { - var negotiation = dataManagementAPI.getNegotiation(id); - return negotiation != null - && Stream.of( - ContractNegotiationState.ERROR, - ContractNegotiationState.CONFIRMED, - ContractNegotiationState.DECLINED) - .anyMatch((l) -> l.equals(negotiation.getState())); - } + + private final String id; + + public Negotiation(String id) { + this.id = Objects.requireNonNull(id); + } + + public void waitUntilComplete(DataManagementAPI dataManagementAPI) { + await() + .pollDelay(Duration.ofMillis(2000)) + .atMost(Timeouts.CONTRACT_NEGOTIATION) + .until(() -> isComplete(dataManagementAPI)); + } + + public boolean isComplete(DataManagementAPI dataManagementAPI) throws IOException { + var negotiation = dataManagementAPI.getNegotiation(id); + return negotiation != null + && Stream.of( + ContractNegotiationState.ERROR, + ContractNegotiationState.CONFIRMED, + ContractNegotiationState.DECLINED) + .anyMatch((l) -> l.equals(negotiation.getState())); + } + + public String getId() { + return id; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/OrConstraint.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/OrConstraint.java index 88bf3438d..66ffd4396 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/OrConstraint.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/OrConstraint.java @@ -1,11 +1,18 @@ package org.eclipse.tractusx.edc.tests.data; import java.util.List; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class OrConstraint implements Constraint { - @NonNull List constraints; + private final List constraints; + + public OrConstraint(List constraints) { + this.constraints = Objects.requireNonNull(constraints); + } + + public List getConstraints() { + return constraints; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java index 4376346b2..1412b8d76 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java @@ -20,13 +20,19 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.Value; - /** * The PayMe constraint should be used when no constraint validation/enforcement in the EDC is * intended. */ -@Value + public class PayMeConstraint implements Constraint { - double amount; + private final double amount; + + public PayMeConstraint(double amount) { + this.amount = amount; + } + + public double getAmount() { + return amount; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java index 128e6686f..e90cbfaf0 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java @@ -20,13 +20,30 @@ package org.eclipse.tractusx.edc.tests.data; import java.util.List; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class Permission { - @NonNull String action; - String target; + private final String action; + private final List constraints; + private final String target; + + + public Permission(String action, List constraints, String target) { + this.action = Objects.requireNonNull(action); + this.constraints = Objects.requireNonNull(constraints); + this.target = target; + } + + public String getAction() { + return action; + } + + public List getConstraints() { + return constraints; + } - @NonNull List constraints; + public String getTarget() { + return target; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java index 27ea65d7a..c58c79206 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java @@ -21,11 +21,23 @@ package org.eclipse.tractusx.edc.tests.data; import java.util.List; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class Policy { - String id; - @NonNull List Permission; + private final String id; + private final List Permission; + + public Policy(String id, List permission) { + this.id = id; + Permission = Objects.requireNonNull(permission); + } + + public String getId() { + return id; + } + + public List getPermission() { + return Permission; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/S3DataAddress.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/S3DataAddress.java index a59843447..93fe5ce8b 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/S3DataAddress.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/S3DataAddress.java @@ -1,12 +1,28 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class S3DataAddress implements DataAddress { - @NonNull String bucketName; - @NonNull String region; - @NonNull String keyName; + private final String bucketName; + private final String region; + private final String keyName; + + public S3DataAddress(String bucketName, String region, String keyName) { + this.bucketName = Objects.requireNonNull(bucketName); + this.region = Objects.requireNonNull(region); + this.keyName = Objects.requireNonNull(keyName); + } + + public String getBucketName() { + return bucketName; + } + + public String getRegion() { + return region; + } + + public String getKeyName() { + return keyName; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Transfer.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Transfer.java index ebd722d07..ea38442a3 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Transfer.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Transfer.java @@ -1,31 +1,41 @@ package org.eclipse.tractusx.edc.tests.data; -import static org.awaitility.Awaitility.await; +import org.eclipse.tractusx.edc.tests.DataManagementAPI; +import org.eclipse.tractusx.edc.tests.util.Timeouts; import java.io.IOException; import java.time.Duration; -import lombok.Value; -import org.eclipse.tractusx.edc.tests.DataManagementAPI; -import org.eclipse.tractusx.edc.tests.util.Timeouts; -@Value +import static org.awaitility.Awaitility.await; + + public class Transfer { - String id; + private final String id; + + public Transfer(String id) { + this.id = id; + } + + public void waitUntilComplete(DataManagementAPI dataManagementAPI) { + await() + .pollDelay(Duration.ofMillis(2000)) + .atMost(Timeouts.FILE_TRANSFER) + .until(() -> isComplete(dataManagementAPI)); + } - public void waitUntilComplete(DataManagementAPI dataManagementAPI) { - await() - .pollDelay(Duration.ofMillis(2000)) - .atMost(Timeouts.FILE_TRANSFER) - .until(() -> isComplete(dataManagementAPI)); - } + public boolean isComplete(DataManagementAPI dataManagementAPI) throws IOException { + var transferProcess = dataManagementAPI.getTransferProcess(id); + if (transferProcess == null) { + return false; + } - public boolean isComplete(DataManagementAPI dataManagementAPI) throws IOException { - var transferProcess = dataManagementAPI.getTransferProcess(id); - if (transferProcess == null) return false; + var state = transferProcess.getState(); - var state = transferProcess.getState(); + return state == TransferProcessState.COMPLETED || state == TransferProcessState.ERROR; + } - return state == TransferProcessState.COMPLETED || state == TransferProcessState.ERROR; - } + public String getId() { + return id; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java index 28be5157a..1c00e86c3 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java @@ -19,11 +19,22 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class TransferProcess { - @NonNull String id; - @NonNull TransferProcessState state; + private final String id; + private final TransferProcessState state; + + public TransferProcess(String id, TransferProcessState state) { + this.id = Objects.requireNonNull(id); + this.state = Objects.requireNonNull(state); + } + + public String getId() { + return id; + } + + public TransferProcessState getState() { + return state; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java index 53aececa9..e03f38e98 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java @@ -24,28 +24,33 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; -import lombok.RequiredArgsConstructor; -@RequiredArgsConstructor + public class DatabaseCleaner { - private static final String SQL = - "DELETE FROM edc_contract_negotiation;\n" - + "DELETE FROM edc_contract_agreement;\n" - + "DELETE FROM edc_transfer_process;\n" - + "DELETE FROM edc_contract_definitions;\n" - + "DELETE FROM edc_policydefinitions;\n" - + "DELETE FROM edc_asset;\n" - + "DELETE FROM edc_lease;"; - - private final String url; - private final String user; - private final String password; - - public void run() throws SQLException { - try (Connection con = DriverManager.getConnection(url, user, password)) { - Statement st = con.createStatement(); - st.executeUpdate(SQL); + private static final String SQL = + "DELETE FROM edc_contract_negotiation;\n" + + "DELETE FROM edc_contract_agreement;\n" + + "DELETE FROM edc_transfer_process;\n" + + "DELETE FROM edc_contract_definitions;\n" + + "DELETE FROM edc_policydefinitions;\n" + + "DELETE FROM edc_asset;\n" + + "DELETE FROM edc_lease;"; + + private final String url; + private final String user; + private final String password; + + public DatabaseCleaner(String url, String user, String password) { + this.url = url; + this.user = user; + this.password = password; + } + + public void run() throws SQLException { + try (Connection con = DriverManager.getConnection(url, user, password)) { + Statement st = con.createStatement(); + st.executeUpdate(SQL); + } } - } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java index c2779ce0d..63ab60324 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java @@ -19,16 +19,9 @@ package org.eclipse.tractusx.edc.tests.util; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.edc.tests.Environment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.core.ResponseBytes; @@ -45,80 +38,89 @@ import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Object; -@Slf4j +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + + public class S3Client { + private static final Logger log = LoggerFactory.getLogger(S3Client.class); + private final software.amazon.awssdk.services.s3.S3Client s3; + + public S3Client(Environment environment) { + + s3 = + software.amazon.awssdk.services.s3.S3Client.builder() + .region(Region.US_EAST_1) + .forcePathStyle(true) + .endpointOverride(URI.create(environment.getAwsEndpointOverride())) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create( + environment.getAwsAccessKey(), environment.getAwsSecretAccessKey()))) + .build(); + } + + public void createBucket(String bucketName) { + try { + s3.createBucket(CreateBucketRequest.builder().bucket(bucketName).build()); + } catch (BucketAlreadyOwnedByYouException e) { + log.info("'{}' bucket already owned - skipped bucket creation", bucketName); + } + } + + public File uploadFile(String bucketName, String fileName) throws IOException { + File tempFile = File.createTempFile(fileName, null); + Files.write( + tempFile.toPath(), "Will fail if the file has no content".getBytes(StandardCharsets.UTF_8)); + + s3.putObject( + PutObjectRequest.builder().bucket(bucketName).key(fileName).build(), + RequestBody.fromFile(tempFile)); + + return tempFile; + } + + public List listBuckets() { + return s3.listBuckets().buckets().stream().map(Bucket::name).collect(Collectors.toList()); + } + + public Set listBucketContent(String bucketName) { + return s3 + .listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) + .contents() + .stream() + .map(S3Object::key) + .collect(Collectors.toSet()); + } + + public File downloadFile(String bucketName, String fileName) throws IOException { + ResponseBytes objectAsBytes = + s3.getObjectAsBytes(GetObjectRequest.builder().bucket(bucketName).key(fileName).build()); + + return Files.write(File.createTempFile(fileName, null).toPath(), objectAsBytes.asByteArray()) + .toFile(); + } + + public void deleteAllBuckets() { + List buckets = s3.listBuckets().buckets(); + buckets.forEach(this::clearBucket); + buckets.forEach( + bucket -> s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucket.name()).build())); + } - private final software.amazon.awssdk.services.s3.S3Client s3; - - public S3Client(Environment environment) { - - s3 = - software.amazon.awssdk.services.s3.S3Client.builder() - .region(Region.US_EAST_1) - .forcePathStyle(true) - .endpointOverride(URI.create(environment.getAwsEndpointOverride())) - .credentialsProvider( - StaticCredentialsProvider.create( - AwsBasicCredentials.create( - environment.getAwsAccessKey(), environment.getAwsSecretAccessKey()))) - .build(); - } - - public void createBucket(String bucketName) { - try { - s3.createBucket(CreateBucketRequest.builder().bucket(bucketName).build()); - } catch (BucketAlreadyOwnedByYouException e) { - log.info("'{}' bucket already owned - skipped bucket creation", bucketName); + private void clearBucket(Bucket bucket) { + String bucketName = bucket.name(); + s3.listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) + .contents() + .forEach( + s3Object -> + s3.deleteObject( + DeleteObjectRequest.builder().bucket(bucketName).key(s3Object.key()).build())); } - } - - public File uploadFile(String bucketName, String fileName) throws IOException { - File tempFile = File.createTempFile(fileName, null); - Files.write( - tempFile.toPath(), "Will fail if the file has no content".getBytes(StandardCharsets.UTF_8)); - - s3.putObject( - PutObjectRequest.builder().bucket(bucketName).key(fileName).build(), - RequestBody.fromFile(tempFile)); - - return tempFile; - } - - public List listBuckets() { - return s3.listBuckets().buckets().stream().map(Bucket::name).collect(Collectors.toList()); - } - - public Set listBucketContent(String bucketName) { - return s3 - .listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) - .contents() - .stream() - .map(S3Object::key) - .collect(Collectors.toSet()); - } - - public File downloadFile(String bucketName, String fileName) throws IOException { - ResponseBytes objectAsBytes = - s3.getObjectAsBytes(GetObjectRequest.builder().bucket(bucketName).key(fileName).build()); - - return Files.write(File.createTempFile(fileName, null).toPath(), objectAsBytes.asByteArray()) - .toFile(); - } - - public void deleteAllBuckets() { - List buckets = s3.listBuckets().buckets(); - buckets.forEach(this::clearBucket); - buckets.forEach( - bucket -> s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucket.name()).build())); - } - - private void clearBucket(Bucket bucket) { - String bucketName = bucket.name(); - s3.listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) - .contents() - .forEach( - s3Object -> - s3.deleteObject( - DeleteObjectRequest.builder().bucket(bucketName).key(s3Object.key()).build())); - } } From ecccbb0333a6d93ab0bbb13c69ed019636d7917b Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Fri, 14 Apr 2023 14:18:43 +0200 Subject: [PATCH 070/132] chore: add a template for pull request descriptions (#213) --- .github/PULL_REQUEST_TEMPLATE.md | 13 +++++++++++++ .github/workflows/verify.yaml | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..fe4467a54 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +## WHAT + +_Briefly describe what your PR changes, which features it adds/modifies._ + +## WHY + +_Briefly state why the change was necessary._ + +## FURTHER NOTES + +_List other areas of code that have changed but are not necessarily linked to the main feature. This could be method signature changes, package declarations, bugs that were encountered and were fixed inline, etc._ + +Closes # <-- _insert Issue number if one exists_ diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index 01064f35d..c844d9249 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -84,7 +84,7 @@ jobs: - name: Run markdownlint run: | - markdownlint-cli2-config .markdownlint.yaml "**/*.md" + markdownlint-cli2-config .markdownlint.yaml "**/*.md" "#.github" unit-tests: runs-on: ubuntu-latest From 406e1373b61e3bde6050e0d36465c0f201b86104 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" <100704677+tuncaytunc-zf@users.noreply.github.com> Date: Fri, 14 Apr 2023 15:04:11 +0200 Subject: [PATCH 071/132] fix: Adapt Helm Chart for version 0.3.x (#211) * Adapt Charts for version 0.3.x * fix business-tests * add edc.receiver.http.dynamic.endpoint * fix business-tests * code-review findings --- .github/workflows/business-tests.yaml | 8 ++-- .../tractusx-connector/templates/_helpers.tpl | 12 +++--- .../templates/deployment-controlplane.yaml | 43 ++++++------------- .../templates/deployment-dataplane.yaml | 24 +++++++---- .../templates/service-controlplane.yaml | 16 +++---- .../templates/service-dataplane.yaml | 4 ++ charts/tractusx-connector/values.yaml | 24 +++++------ .../edc-dataplane-base/build.gradle.kts | 22 +++++----- 8 files changed, 72 insertions(+), 81 deletions(-) diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 7c64b29f5..248927db0 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -128,14 +128,14 @@ jobs: run: |- # Define endpoints echo "SOKRATES_DATA_MANAGEMENT_API_AUTH_KEY=password" | tee -a ${GITHUB_ENV} - echo "SOKRATES_DATA_MANAGEMENT_URL=http://sokrates-controlplane:8081/data" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATA_MANAGEMENT_URL=http://sokrates-controlplane:8081/management" | tee -a ${GITHUB_ENV} echo "SOKRATES_IDS_URL=http://sokrates-controlplane:8084/api/v1/ids" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATA_PLANE_URL=http://sokrates-dataplane:8081/api/public/" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_URL=jdbc:postgresql://plato-postgresql:5432/edc" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_USER=user" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_PASSWORD=password" | tee -a ${GITHUB_ENV} echo "PLATO_DATA_MANAGEMENT_API_AUTH_KEY=password" | tee -a ${GITHUB_ENV} - echo "PLATO_DATA_MANAGEMENT_URL=http://plato-controlplane:8081/data" | tee -a ${GITHUB_ENV} + echo "PLATO_DATA_MANAGEMENT_URL=http://plato-controlplane:8081/management" | tee -a ${GITHUB_ENV} echo "PLATO_IDS_URL=http://plato-controlplane:8084/api/v1/ids" | tee -a ${GITHUB_ENV} echo "PLATO_DATA_PLANE_URL=http://plato-dataplane:8081/api/public/" | tee -a ${GITHUB_ENV} echo "PLATO_DATABASE_URL=jdbc:postgresql://plato-postgresql:5432/edc" | tee -a ${GITHUB_ENV} @@ -176,7 +176,7 @@ jobs: helm install plato charts/tractusx-connector \ --set fullnameOverride=plato \ --set controlplane.service.type=NodePort \ - --set controlplane.endpoints.data.authKey=password \ + --set controlplane.endpoints.management.authKey=password \ --set controlplane.image.tag=business-test \ --set controlplane.image.pullPolicy=Never \ --set controlplane.image.repository=docker.io/library/edc-controlplane-postgresql-hashicorp-vault \ @@ -209,7 +209,7 @@ jobs: helm install sokrates charts/tractusx-connector \ --set fullnameOverride=sokrates \ --set controlplane.service.type=NodePort \ - --set controlplane.endpoints.data.authKey=password \ + --set controlplane.endpoints.management.authKey=password \ --set controlplane.image.tag=business-test \ --set controlplane.image.pullPolicy=Never \ --set controlplane.image.repository=docker.io/library/edc-controlplane-postgresql-hashicorp-vault \ diff --git a/charts/tractusx-connector/templates/_helpers.tpl b/charts/tractusx-connector/templates/_helpers.tpl index ecc8ff1d2..701e6fc75 100644 --- a/charts/tractusx-connector/templates/_helpers.tpl +++ b/charts/tractusx-connector/templates/_helpers.tpl @@ -110,9 +110,9 @@ Create the name of the service account to use {{/* Control IDS URL */}} -{{- define "txdc.controlplane.url.ids" -}} -{{- if .Values.controlplane.url.ids }}{{/* if ids api url has been specified explicitly */}} -{{- .Values.controlplane.url.ids }} +{{- define "txdc.controlplane.url.protocol" -}} +{{- if .Values.controlplane.url.protocol }}{{/* if ids api url has been specified explicitly */}} +{{- .Values.controlplane.url.protocol }} {{- else }}{{/* else when ids api url has not been specified explicitly */}} {{- with (index .Values.controlplane.ingresses 0) }} {{- if .enabled }}{{/* if ingress enabled */}} @@ -122,17 +122,17 @@ Control IDS URL {{- printf "http://%s" .hostname -}} {{- end }}{{/* end if tls */}} {{- else }}{{/* else when ingress not enabled */}} -{{- printf "http://%s-controlplane:%v" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.ids.port -}} +{{- printf "http://%s-controlplane:%v" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.protocol.port -}} {{- end }}{{/* end if ingress */}} {{- end }}{{/* end with ingress */}} -{{- end }}{{/* end if .Values.controlplane.url.ids */}} +{{- end }}{{/* end if .Values.controlplane.url.protocol */}} {{- end }} {{/* Validation URL */}} {{- define "txdc.controlplane.url.validation" -}} -{{- printf "http://%s-controlplane:%v%s/token" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.validation.port $.Values.controlplane.endpoints.validation.path -}} +{{- printf "http://%s-controlplane:%v%s/token" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.control.port $.Values.controlplane.endpoints.control.path -}} {{- end }} {{/* diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index dc708a8a7..338feebe3 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -128,45 +128,30 @@ spec: value: {{ printf "%s%s" .Values.daps.url .Values.daps.paths.token }} - name: EDC_OAUTH_PRIVATE_KEY_ALIAS value: {{ .Values.vault.secretNames.dapsPrivateKey | required ".Values.vault.secretNames.dapsPrivateKey is required" | quote }} - - name: EDC_OAUTH_PUBLIC_KEY_ALIAS + - name: EDC_OAUTH_CERTIFICATE_ALIAS value: {{ .Values.vault.secretNames.dapsPublicKey | required ".Values.vault.secretNames.dapsPublicKey is required" | quote }} ####### # API # ####### - name: "EDC_API_AUTH_KEY" - value: {{ .Values.controlplane.endpoints.data.authKey | required ".Values.controlplane.endpoints.data.authKey is required" | quote }} + value: {{ .Values.controlplane.endpoints.management.authKey | required ".Values.controlplane.endpoints.mangement.authKey is required" | quote }} - name: "WEB_HTTP_DEFAULT_PORT" value: {{ .Values.controlplane.endpoints.default.port | quote }} - name: "WEB_HTTP_DEFAULT_PATH" value: {{ .Values.controlplane.endpoints.default.path | quote }} - {{- if or (eq (substr 0 3 .Values.controlplane.image.tag) "0.1") (eq (substr 0 3 .Values.controlplane.image.tag) "0.2") }} - # WEB_HTTP_DATA_PORT is renamed to WEB_HTTP_MANAGEMENT_PORT from version 0.2.1 and newer - # we will keep both settings for downward capabilities - - name: "WEB_HTTP_DATA_PORT" - value: {{ .Values.controlplane.endpoints.data.port | quote }} - # WEB_HTTP_DATA_PATH is renamed to WEB_HTTP_MANAGEMENT_PATH from version 0.2.1 and newer - # we will keep both settings for downward capabilities - - name: "WEB_HTTP_DATA_PATH" - value: {{ .Values.controlplane.endpoints.data.path | quote }} - {{- else }} - name: "WEB_HTTP_MANAGEMENT_PORT" - value: {{ .Values.controlplane.endpoints.data.port | quote }} + value: {{ .Values.controlplane.endpoints.management.port | quote }} - name: "WEB_HTTP_MANAGEMENT_PATH" - value: {{ .Values.controlplane.endpoints.data.path | quote }} - {{- end }} - - name: "WEB_HTTP_VALIDATION_PORT" - value: {{ .Values.controlplane.endpoints.validation.port | quote }} - - name: "WEB_HTTP_VALIDATION_PATH" - value: {{ .Values.controlplane.endpoints.validation.path | quote }} + value: {{ .Values.controlplane.endpoints.management.path | quote }} - name: "WEB_HTTP_CONTROL_PORT" value: {{ .Values.controlplane.endpoints.control.port | quote }} - name: "WEB_HTTP_CONTROL_PATH" value: {{ .Values.controlplane.endpoints.control.path | quote }} - - name: "WEB_HTTP_IDS_PORT" - value: {{ .Values.controlplane.endpoints.ids.port | quote }} - - name: "WEB_HTTP_IDS_PATH" - value: {{ .Values.controlplane.endpoints.ids.path | quote }} + - name: "WEB_HTTP_PROTOCOL_PORT" + value: {{ .Values.controlplane.endpoints.protocol.port | quote }} + - name: "WEB_HTTP_PROTOCOL_PATH" + value: {{ .Values.controlplane.endpoints.protocol.path | quote }} - name: "WEB_HTTP_OBSERVABILITY_PORT" value: {{ .Values.controlplane.endpoints.observability.port | quote}} - name: "WEB_HTTP_OBSERVABILITY_PATH" @@ -178,9 +163,9 @@ spec: ## IDS ## ######### - name: "IDS_WEBHOOK_ADDRESS" - value: {{ include "txdc.controlplane.url.ids" . | quote }} + value: {{ include "txdc.controlplane.url.protocol" . | quote }} - name: "EDC_IDS_ENDPOINT" - value: {{ printf "%s%s" (include "txdc.controlplane.url.ids" .) .Values.controlplane.endpoints.ids.path | quote }} + value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" .) .Values.controlplane.endpoints.protocol.path | quote }} - name: "EDC_IDS_ID" value: {{ printf "urn:connector:%s" (lower .Values.controlplane.internationalDataSpaces.id) | quote }} - name: "EDC_IDS_DESCRIPTION" @@ -196,10 +181,10 @@ spec: - name: "EDC_OAUTH_PROVIDER_AUDIENCE" value: "idsc:IDS_CONNECTORS_ALL" - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.controlplane.url.ids" . ) .Values.controlplane.endpoints.ids.path "/data" | quote }} + value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} # this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older - name: "EDC_IDS_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.controlplane.url.ids" . ) .Values.controlplane.endpoints.ids.path "/data" | quote }} + value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} {{- if .Values.postgresql.enabled }} @@ -281,9 +266,9 @@ spec: - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/http-receiver + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/transfer/transfer-pull-http-dynamic-receiver - - name: "EDC_RECEIVER_HTTP_ENDPOINT" + - name: "EDC_RECEIVER_HTTP_DYNAMIC_ENDPOINT" value: {{ .Values.backendService.httpProxyTokenReceiverUrl | required ".Values.backendService.httpProxyTokenReceiverUrl is required" | quote }} ########### diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index bd375b295..c83742cba 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -78,8 +78,8 @@ spec: {{- if .Values.dataplane.livenessProbe.enabled }} livenessProbe: httpGet: - path: {{ .Values.dataplane.endpoints.default.path }}/check/liveness - port: {{ .Values.dataplane.endpoints.default.port }} + path: {{ .Values.dataplane.endpoints.observability.path }}/check/liveness + port: {{ .Values.dataplane.endpoints.observability.port }} initialDelaySeconds: {{ .Values.dataplane.livenessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.dataplane.livenessProbe.periodSeconds }} timeoutSeconds: {{ .Values.dataplane.livenessProbe.timeoutSeconds }} @@ -89,8 +89,8 @@ spec: {{- if .Values.dataplane.readinessProbe.enabled }} readinessProbe: httpGet: - path: {{ .Values.dataplane.endpoints.default.path }}/check/readiness - port: {{ .Values.dataplane.endpoints.default.port }} + path: {{ .Values.dataplane.endpoints.observability.path }}/check/readiness + port: {{ .Values.dataplane.endpoints.observability.port }} initialDelaySeconds: {{ .Values.dataplane.readinessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.dataplane.readinessProbe.periodSeconds }} timeoutSeconds: {{ .Values.dataplane.readinessProbe.timeoutSeconds }} @@ -122,16 +122,18 @@ spec: value: {{ .Values.dataplane.endpoints.control.port | quote }} - name: "WEB_HTTP_CONTROL_PATH" value: {{ .Values.dataplane.endpoints.control.path | quote }} - - name: "WEB_HTTP_VALIDATION_PORT" - value: {{ .Values.dataplane.endpoints.validation.port | quote }} - - name: "WEB_HTTP_VALIDATION_PATH" - value: {{ .Values.dataplane.endpoints.validation.path | quote }} - name: "WEB_HTTP_PUBLIC_PORT" value: {{ .Values.dataplane.endpoints.public.port | quote }} - name: "WEB_HTTP_PUBLIC_PATH" value: {{ .Values.dataplane.endpoints.public.path | quote }} - name: "EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT" value: {{ include "txdc.controlplane.url.validation" .}} + - name: "WEB_HTTP_OBSERVABILITY_PORT" + value: {{ .Values.dataplane.endpoints.observability.port | quote }} + - name: "WEB_HTTP_OBSERVABILITY_PATH" + value: {{ .Values.dataplane.endpoints.observability.path | quote }} + - name: "TRACTUSX_API_OBSERVABILITY_ALLOW-INSECURE" + value: {{ .Values.dataplane.endpoints.observability.insecure | quote }} ####### # AWS # @@ -178,10 +180,16 @@ spec: value: {{ .Values.vault.azure.tenant | quote }} - name: "EDC_VAULT_NAME" value: {{ .Values.vault.azure.name | quote }} + # only set the env var if config value not null + {{- if .Values.vault.azure.secret }} - name: "EDC_VAULT_CLIENTSECRET" value: {{ .Values.vault.azure.secret | quote }} + {{- end }} + # only set the env var if config value not null + {{- if .Values.vault.azure.certificate }} - name: "EDC_VAULT_CERTIFICATE" value: {{ .Values.vault.azure.certificate | quote }} + {{- end }} {{- end }} ###################################### diff --git a/charts/tractusx-connector/templates/service-controlplane.yaml b/charts/tractusx-connector/templates/service-controlplane.yaml index 94a02fa1e..acab58343 100644 --- a/charts/tractusx-connector/templates/service-controlplane.yaml +++ b/charts/tractusx-connector/templates/service-controlplane.yaml @@ -39,18 +39,14 @@ spec: targetPort: control protocol: TCP name: control - - port: {{ .Values.controlplane.endpoints.data.port }} - targetPort: data + - port: {{ .Values.controlplane.endpoints.management.port }} + targetPort: management protocol: TCP - name: data - - port: {{ .Values.controlplane.endpoints.validation.port }} - targetPort: validation + name: management + - port: {{ .Values.controlplane.endpoints.protocol.port }} + targetPort: protocol protocol: TCP - name: validation - - port: {{ .Values.controlplane.endpoints.ids.port }} - targetPort: ids - protocol: TCP - name: ids + name: protocol - port: {{ .Values.controlplane.endpoints.metrics.port }} targetPort: metrics protocol: TCP diff --git a/charts/tractusx-connector/templates/service-dataplane.yaml b/charts/tractusx-connector/templates/service-dataplane.yaml index 26fa9c203..5644f7fbe 100644 --- a/charts/tractusx-connector/templates/service-dataplane.yaml +++ b/charts/tractusx-connector/templates/service-dataplane.yaml @@ -21,6 +21,10 @@ spec: targetPort: public protocol: TCP name: public + - port: {{ .Values.dataplane.endpoints.observability.port }} + targetPort: observability + protocol: TCP + name: observability - port: {{ .Values.dataplane.endpoints.metrics.port }} targetPort: metrics protocol: TCP diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index cbc266a94..aebd45481 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -89,19 +89,13 @@ controlplane: # -- path for incoming api calls path: /api # -- data management api, used by internal users, can be added to an ingress and must not be internet facing - data: + management: # -- port for incoming api calls port: 8081 # -- path for incoming api calls - path: /data + path: /management # -- authentication key, must be attached to each 'X-Api-Key' request header authKey: "" - # -- validation api, only used by the data plane and should not be added to any ingress - validation: - # -- port for incoming api calls - port: 8082 - # -- path for incoming api calls - path: /validation # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not control: # -- port for incoming api calls @@ -109,7 +103,7 @@ controlplane: # -- path for incoming api calls path: /control # -- ids api, used for inter connector communication and must be internet facing - ids: + protocol: # -- port for incoming api calls port: 8084 # -- path for incoming api calls @@ -221,7 +215,7 @@ controlplane: annotations: {} # -- EDC endpoints exposed by this ingress resource endpoints: - - data + - management - control # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" @@ -340,12 +334,16 @@ dataplane: public: port: 8081 path: /api/public - validation: - port: 8082 - path: /validation control: port: 8083 path: /api/dataplane/control + observability: + # -- port for incoming API calls + port: 8085 + # -- observability api, provides /health /readiness and /liveness endpoints + path: /observability + # -- allow or disallow insecure access, i.e. access without authentication + insecure: true metrics: port: 9090 path: /metrics diff --git a/edc-dataplane/edc-dataplane-base/build.gradle.kts b/edc-dataplane/edc-dataplane-base/build.gradle.kts index 686e5fd06..cc873dcea 100644 --- a/edc-dataplane/edc-dataplane-base/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-base/build.gradle.kts @@ -4,18 +4,18 @@ plugins { } dependencies { - implementation(edc.config.filesystem) - implementation(edc.dpf.awss3) - implementation(edc.dpf.oauth2) - implementation(edc.dpf.http) + runtimeOnly(project(":edc-extensions:observability-api-customization")) - implementation(edc.dpf.framework) - implementation(edc.dpf.api) - implementation(edc.api.observability) - implementation(edc.core.connector) - implementation(edc.boot) + runtimeOnly(edc.config.filesystem) + runtimeOnly(edc.dpf.awss3) + runtimeOnly(edc.dpf.oauth2) + runtimeOnly(edc.dpf.http) + runtimeOnly(edc.dpf.framework) + runtimeOnly(edc.dpf.api) + runtimeOnly(edc.core.connector) + runtimeOnly(edc.boot) - implementation(edc.bundles.monitoring) - implementation(edc.ext.http) + runtimeOnly(edc.bundles.monitoring) + runtimeOnly(edc.ext.http) } \ No newline at end of file From e566646168b401a7bdc632619ef67502810a8851 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Sat, 15 Apr 2023 07:49:08 +0200 Subject: [PATCH 072/132] refactor: rename git branches (#218) * refactor: update branch names and references in our documentation * publish packages to tractus-x --- .../actions/publish-docker-image/action.yml | 1 + .github/dependabot.yml | 16 +++++----- .github/workflows/build.yaml | 23 ++++++++------- .github/workflows/business-tests.yaml | 5 ++-- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/helm-chart-release.yaml | 2 +- .github/workflows/helm-lint.yaml | 4 +-- .github/workflows/kics.yml | 4 +-- .github/workflows/publish-docker.yaml | 3 +- .github/workflows/publish-new-release.yml | 29 +++++++++---------- .github/workflows/trivy.yml | 4 +-- .github/workflows/verify.yaml | 3 +- .../templates/deployment-controlplane.yaml | 6 ++-- .../templates/deployment-dataplane.yaml | 2 +- .../2023-02-09-release-process/README.md | 18 ++++++------ .../2023-02-27_testing/README.md | 2 +- .../notice.md | 4 +-- .../edc-controlplane-memory/notice.md | 4 +-- .../notice.md | 4 +-- .../edc-controlplane-postgresql/notice.md | 4 +-- .../edc-dataplane-azure-vault/notice.md | 4 +-- .../edc-dataplane-hashicorp-vault/notice.md | 4 +-- pr_etiquette.md | 2 +- 23 files changed, 77 insertions(+), 73 deletions(-) diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 0dfefd5f0..600383f81 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -86,6 +86,7 @@ runs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{raw}} type=match,pattern=\d.\d.\d + type=raw,value=latest,enable={{is_default_branch}} type=sha ############################### diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 16f8582cb..b59a06386 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,8 +3,8 @@ version: 2 updates: # Maven - - package-ecosystem: "maven" - target-branch: develop + package-ecosystem: "gradle" + target-branch: main directory: / labels: - "dependabot" @@ -15,7 +15,7 @@ updates: # Github Actions - package-ecosystem: "github-actions" - target-branch: develop + target-branch: main directory: / labels: - "dependabot" @@ -26,7 +26,7 @@ updates: # Docker - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/ labels: - "dependabot" @@ -35,7 +35,7 @@ updates: interval: "daily" - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-controlplane/edc-controlplane-postgresql/src/main/docker/ labels: - "dependabot" @@ -44,7 +44,7 @@ updates: interval: "daily" - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-controlplane/edc-controlplane-memory/src/main/docker/ labels: - "dependabot" @@ -53,7 +53,7 @@ updates: interval: "daily" - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-dataplane/edc-dataplane-azure-vault/src/main/docker/ labels: - "dependabot" @@ -62,7 +62,7 @@ updates: interval: "daily" - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/ labels: - "dependabot" diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f3e63414a..76d0e01c5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -27,7 +27,7 @@ on: push: branches: - main - - develop + - releases tags: - '[0-9]+.[0-9]+.[0-9]+' release: @@ -44,7 +44,8 @@ on: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # cancel only running jobs on pull requests + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: @@ -136,9 +137,9 @@ jobs: packages: write needs: [ secret-presence, build-controlplane, build-dataplane, build-extensions ] - # do not run on PR branches, do not run on main + # do not run on PR branches, do not run on releases if: | - needs.secret-presence.outputs.GPG_PASSPHRASE && needs.secret-presence.outputs.GPG_PRIVATE_KEY && github.event_name != 'pull_request' && github.ref != 'refs/heads/main' + needs.secret-presence.outputs.GPG_PASSPHRASE && needs.secret-presence.outputs.GPG_PRIVATE_KEY && github.event_name != 'pull_request' && github.ref != 'refs/heads/releases' steps: # Set-Up - name: Checkout @@ -152,9 +153,9 @@ jobs: cache: 'gradle' - name: Import GPG Key uses: crazy-max/ghaction-import-gpg@v5 - env: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} # publish snapshots - name: Publish snapshot versions @@ -162,7 +163,7 @@ jobs: echo "Publishing Version $(grep -e "version" gradle.properties | cut -f2 -d"=") to Github Packages" ./gradlew publishAllPublicationsToGitHubPackagesRepository env: - #REPO: ${{ github.repository }} - REPO: "catenax-ng/product-edc" - GITHUB_PACKAGE_USERNAME: ${{ secrets.TEMP_GHPKG_USER }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.TEMP_GHPKG_PASSWORD }} + REPO: ${{ github.repository }} + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 248927db0..97a3044fb 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -29,13 +29,14 @@ on: - 'docs/**' - '**/*.md' branches: - - develop + - releases - release/** - main workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # cancel only running jobs on pull requests + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 82e956c76..248f61bc4 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -79,7 +79,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: head: release/${{ github.event.inputs.version }} - base: main + base: releases title: Release version ${{ github.event.inputs.version }} reviewers: ${{ github.actor }} body: |- diff --git a/.github/workflows/helm-chart-release.yaml b/.github/workflows/helm-chart-release.yaml index 819f4f0ec..bd5e55302 100644 --- a/.github/workflows/helm-chart-release.yaml +++ b/.github/workflows/helm-chart-release.yaml @@ -23,7 +23,7 @@ on: paths: - 'charts/**' branches: - - main + - releases workflow_dispatch: jobs: diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index bf1531cad..ae94c84a7 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -5,7 +5,7 @@ on: push: branches: - main - - develop + - releases tags: - '[0-9]+.[0-9]+.[0-9]+' paths-ignore: @@ -50,7 +50,7 @@ jobs: name: chart-testing (list-changed) id: list-changed run: | - changed=$(ct list-changed --config ct.yaml --target-branch develop) + changed=$(ct list-changed --config ct.yaml --target-branch main) if [[ -n "$changed" ]]; then echo "::set-output name=changed::true" fi diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index c009ab2de..1b922064a 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -2,9 +2,9 @@ name: "KICS" on: push: - branches: [main, master, develop] + branches: [main, releases] pull_request: - branches: [main, master, develop] + branches: [main, releases] workflow_dispatch: schedule: diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index 4bb7a4045..e2a7a5384 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -30,7 +30,8 @@ on: default: "tractusx" concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # cancel only running jobs on pull requests + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index c626bba84..373c892e7 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -4,7 +4,7 @@ name: "Publish new release" on: pull_request: branches: - - main + - releases - support/* types: - closed @@ -67,18 +67,17 @@ jobs: - name: Import GPG Key uses: crazy-max/ghaction-import-gpg@v5 env: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} - name: Publish release version run: | echo "Publishing Version $(grep -e "version" gradle.properties | cut -f2 -d"=") to Github Packages" ./gradlew publishAllPublicationsToGithubPackagesRepository env: - #REPO: ${{ github.repository }} - REPO: "catenax-ng/product-edc" - GITHUB_PACKAGE_USERNAME: ${{ secrets.TEMP_GHPKG_USER }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.TEMP_GHPKG_PASSWORD }} + REPO: ${{ github.repository }} + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} # Release: Helm Charts helm-release: @@ -128,7 +127,7 @@ jobs: git push origin gh-pages - # Release: GitHub tag & release; Merges back main into develop; Starts a new development cycle; + # Release: GitHub tag & release; Merges back releases into main; Starts a new development cycle; github-release: name: Publish new github release needs: [ release-version ] @@ -148,7 +147,7 @@ jobs: name: Checkout uses: actions/checkout@v3.3.0 with: - # 0 to fetch the full history due to upcoming merge of main into develop branch + # 0 to fetch the full history due to upcoming merge of releases into main branch fetch-depth: 0 - name: Create Release Tag @@ -185,15 +184,15 @@ jobs: distribution: 'temurin' cache: 'gradle' - - name: Merge main back into develop and set new snapshot version - if: github.event.pull_request.base.ref == 'main' + name: Merge releases back into main and set new snapshot version + if: github.event.pull_request.base.ref == 'releases' run: | # Prepare git env git config user.name "GitHub actions" git config user.email noreply@github.com - # Merge main into develop - git checkout develop && git merge -X theirs main --no-commit --no-ff + # Merge releases into main + git checkout main && git merge -X theirs releases --no-commit --no-ff # Extract release version IFS=. read -r RELEASE_VERSION_MAJOR RELEASE_VERSION_MINOR RELEASE_VERSION_PATCH<<<"${{ env.RELEASE_VERSION }}" @@ -204,8 +203,8 @@ jobs: # Persist the "version" in the gradle.properties sed -i "s/version=.*/version=$SNAPSHOT_VERSION/g" gradle.properties - # Commit and push to origin develop + # Commit and push to origin main git add gradle.properties git commit --message "Introduce new snapshot version $SNAPSHOT_VERSION" - git push origin develop + git push origin main diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index b82acaf66..714ed4c74 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -9,7 +9,7 @@ on: workflows: ["Build"] branches: - main - - develop + - releases - release/* - hotfix/* tags: @@ -84,7 +84,7 @@ jobs: if: always() uses: aquasecurity/trivy-action@master with: - image-ref: "ghcr.io/${{ github.repository }}/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }}" + image-ref: "tractusx/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }}" format: "sarif" output: "trivy-results-${{ matrix.image }}.sarif" exit-code: "1" diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index c844d9249..d9dda3844 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -25,7 +25,7 @@ on: push: branches: - main - - develop + - releases tags: - '[0-9]+.[0-9]+.[0-9]+' release: @@ -39,6 +39,7 @@ on: workflow_dispatch: concurrency: + # cancel older running jobs on the same branch group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 338feebe3..1161db178 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -247,7 +247,7 @@ spec: ## DATA PLANE ## ################ - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/dataplane-selector-configuration + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/dataplane-selector-configuration - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" value: {{ include "txdc.dataplane.url.control" . }}/transfer - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" @@ -276,7 +276,7 @@ spec: ########### {{- if .Values.vault.hashicorp.enabled }} - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault - name: "EDC_VAULT_HASHICORP_URL" value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} - name: "EDC_VAULT_HASHICORP_TOKEN" @@ -317,7 +317,7 @@ spec: ## DATA ENCRYPTION ## ##################### - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/data-encryption + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/data-encryption - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} - name: "EDC_DATA_ENCRYPTION_ALGORITHM" diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index c83742cba..bbc48c434 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -156,7 +156,7 @@ spec: ########### {{- if .Values.vault.hashicorp.enabled }} - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault - name: "EDC_VAULT_HASHICORP_URL" value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} - name: "EDC_VAULT_HASHICORP_TOKEN" diff --git a/docs/development/decision-records/2023-02-09-release-process/README.md b/docs/development/decision-records/2023-02-09-release-process/README.md index aee5bac5a..4b2771c0a 100644 --- a/docs/development/decision-records/2023-02-09-release-process/README.md +++ b/docs/development/decision-records/2023-02-09-release-process/README.md @@ -8,7 +8,7 @@ To improve stability, reproducibility and maintainability of releases, tractusx- - use release versions of EDC in releases. Release branches must not change upstream dependency versions, unless there is a clear and concise reason to do so. - slightly update branching model -- if possible, bugs/defects should be fixed on `develop` and be backported to the respective `hotfix/` branch +- if possible, bugs/defects should be fixed on `main` and be backported to the respective `hotfix/` branch - only hotfixes for critical security bugs will be provided as defined by the committers for the currently released version. Nothing else. - feature development happens _in developers' forks only_ to keep the Git reflog of the `origin` clean. @@ -31,15 +31,15 @@ Every release version published by tractusx-edc must be reproducible at any time During feature development we only use `-SNAPSHOT` versions of EDC packages. It is assumed that when the build breaks due to changes in upstream, the fix can be done quickly and easily, much more so than working off technical -debt that would otherwise accumulate over several months. Builds on `develop` are therefore _not repeatable_, but that +debt that would otherwise accumulate over several months. Builds on `main` are therefore _not repeatable_, but that downside is easily offset by the tighter alignment with and smaller technical debt and integration pain with the upstream EDC. ### Use release versions of EDC in releases -First, a new branch `releases/X.Y.Z` based off of `develop` is created. This can either be done +First, a new branch `release/X.Y.Z` based off of `main` is created. This can either be done on `HEAD`, or - if desired - on a particular ref. The latter case is relevant if there are already features -in `develop` that are not scoped for a particular release. +in `main` that are not scoped for a particular release. Second, the dependency onto EDC is updated to the most recent build. For example, if a release is created on March 27th 2023, the most recent nightly would be `0.0.1-20230326`. @@ -79,13 +79,13 @@ Once a release is published, for example `0.3.1` it will receive no further deve hotfix branches are created based off of the release branch, here `releases/0.3.1`, thus, `hotfix/0.3.1`. From this, three scenarios emerge: -1. The actual fix is done on `develop` and can be cherry-picked into the `hotfix/0.3.1` branch. No new commits are +1. The actual fix is done on `main` and can be cherry-picked into the `hotfix/0.3.1` branch. No new commits are made directly in that branch. -2. The actual fix is done on `develop` and must be manually ported into the `hotfix/0.3.1` branch. One or several new +2. The actual fix is done on `main` and must be manually ported into the `hotfix/0.3.1` branch. One or several new commits are made on `hotfix/0.3.1`. This is needed when cherry-picking is not available due to incompatibilities - between `develop` and the hotfix branch due to intermittent changes. -3. The fix is only relevant for the `0.3.1` hotfix, it is not needed in `develop`. This can happen, when the problem is - not present on `develop`, because it was already implicitly fixed, or otherwise doesn't exist. + between `main` and the hotfix branch due to intermittent changes. +3. The fix is only relevant for the `0.3.1` hotfix, it is not needed in `main`. This can happen, when the problem is + not present on `main`, because it was already implicitly fixed, or otherwise doesn't exist. This might produce many branches, and the first `hotfix` makes the release obsolete, but it will greatly help readability and make a release's history readily apparent. diff --git a/docs/development/decision-records/2023-02-27_testing/README.md b/docs/development/decision-records/2023-02-27_testing/README.md index 45844203d..0d12ab353 100644 --- a/docs/development/decision-records/2023-02-27_testing/README.md +++ b/docs/development/decision-records/2023-02-27_testing/README.md @@ -82,5 +82,5 @@ This section explains _at which point in time_ we should execute which test. Thi | Unit test | when running tests locally, without any parameters, on every commit on every branch | | | Integration test | on every commit on every branch | | | System/End-To-End test | on pull request branches except when marked as `draft` | | -| Deployment test | before merging pull requests and on every commit on `develop` | | +| Deployment test | before merging pull requests and on every commit on `main` | | | Performance test | Only on a specific schedule, e.g. once per day or week | | diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md index fdcc88583..e6088f521 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md @@ -10,8 +10,8 @@ Eclipse Tractus-X product(s) installed within the image: - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile -- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) ## Used base image diff --git a/edc-controlplane/edc-controlplane-memory/notice.md b/edc-controlplane/edc-controlplane-memory/notice.md index cee9fe5ed..c58f81b30 100644 --- a/edc-controlplane/edc-controlplane-memory/notice.md +++ b/edc-controlplane/edc-controlplane-memory/notice.md @@ -10,8 +10,8 @@ Eclipse Tractus-X product(s) installed within the image: - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile -- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) ## Used base image diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md index 3b5e517f0..a1c9978ab 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md @@ -10,8 +10,8 @@ Eclipse Tractus-X product(s) installed within the image: - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile -- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) ## Used base image diff --git a/edc-controlplane/edc-controlplane-postgresql/notice.md b/edc-controlplane/edc-controlplane-postgresql/notice.md index d9e1b58b1..e1662d799 100644 --- a/edc-controlplane/edc-controlplane-postgresql/notice.md +++ b/edc-controlplane/edc-controlplane-postgresql/notice.md @@ -10,8 +10,8 @@ Eclipse Tractus-X product(s) installed within the image: - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile -- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) ## Used base image diff --git a/edc-dataplane/edc-dataplane-azure-vault/notice.md b/edc-dataplane/edc-dataplane-azure-vault/notice.md index 7023f7ab7..065833ed2 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/notice.md +++ b/edc-dataplane/edc-dataplane-azure-vault/notice.md @@ -10,8 +10,8 @@ Eclipse Tractus-X product(s) installed within the image: - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile -- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) ## Used base image diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md index 8b18d0a4b..054c5e35f 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md @@ -10,8 +10,8 @@ Eclipse Tractus-X product(s) installed within the image: - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile -- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/develop/LICENSE) +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) ## Used base image diff --git a/pr_etiquette.md b/pr_etiquette.md index aaaf16761..348ebf000 100644 --- a/pr_etiquette.md +++ b/pr_etiquette.md @@ -10,7 +10,7 @@ Submitting pull requests in EDC should be done while adhering to a couple of sim - No surprise PRs please. Before you submit a PR, open a discussion or an issue outlining your planned work and give people time to comment. It may even be advisable to contact committers using the `@mention` feature. Unsolicited PRs may get ignored or rejected. -- Create your working branch in your fork of TractusX-EDC, and create the PR against the upstream `develop` branch +- Create your working branch in your fork of TractusX-EDC, and create the PR against the upstream `main` branch - Create focused PRs: your work should be focused on one particular feature or bug. Do not create broad-scoped PRs that solve multiple issues as reviewers may reject those PR bombs outright. - Provide a clear description and motivation in the PR description in GitHub. This makes the reviewer's life much From 85876568a12590406251ce0ecbab1de38e3d0b48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 07:29:23 +0200 Subject: [PATCH 073/132] chore(deps): bump io.cucumber:cucumber-junit-platform-engine from 7.11.1 to 7.11.2 (#221) * refactor: rename git branches (#218) * refactor: update branch names and references in our documentation * publish packages to tractus-x * chore(deps): bump io.cucumber:cucumber-junit-platform-engine Bumps [io.cucumber:cucumber-junit-platform-engine](https://github.com/cucumber/cucumber-jvm) from 7.11.1 to 7.11.2. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.11.1...v7.11.2) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit-platform-engine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-tests/cucumber/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index 69ba71aa5..431ebcbb7 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -20,7 +20,7 @@ dependencies { testImplementation("org.apache.httpcomponents:httpclient:4.5.14") testImplementation("org.junit.platform:junit-platform-suite:1.9.2") testImplementation("io.cucumber:cucumber-java:7.11.1") - testImplementation("io.cucumber:cucumber-junit-platform-engine:7.11.1") + testImplementation("io.cucumber:cucumber-junit-platform-engine:7.11.2") testImplementation("org.slf4j:slf4j-api:2.0.3") testImplementation(libs.restAssured) testImplementation(libs.postgres) From 568b0cf29b3f85c40b2530ffac8103c92471a96d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 07:56:38 +0200 Subject: [PATCH 074/132] chore(deps): bump io.cucumber:cucumber-java from 7.11.1 to 7.11.2 (#225) Bumps [io.cucumber:cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.11.1 to 7.11.2. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.11.1...v7.11.2) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-tests/cucumber/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index 431ebcbb7..17f5233d7 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { testImplementation("com.google.code.gson:gson:2.10") testImplementation("org.apache.httpcomponents:httpclient:4.5.14") testImplementation("org.junit.platform:junit-platform-suite:1.9.2") - testImplementation("io.cucumber:cucumber-java:7.11.1") + testImplementation("io.cucumber:cucumber-java:7.11.2") testImplementation("io.cucumber:cucumber-junit-platform-engine:7.11.2") testImplementation("org.slf4j:slf4j-api:2.0.3") testImplementation(libs.restAssured) From d47f39e6ce7e27f36952ab21374cbfe04bb3d353 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 07:56:48 +0200 Subject: [PATCH 075/132] chore(deps): bump org.testcontainers:junit-jupiter from 1.17.6 to 1.18.0 (#224) Bumps [org.testcontainers:junit-jupiter](https://github.com/testcontainers/testcontainers-java) from 1.17.6 to 1.18.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.17.6...1.18.0) --- updated-dependencies: - dependency-name: org.testcontainers:junit-jupiter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-extensions/hashicorp-vault/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-extensions/hashicorp-vault/build.gradle.kts b/edc-extensions/hashicorp-vault/build.gradle.kts index 90758edd0..8433e74a3 100644 --- a/edc-extensions/hashicorp-vault/build.gradle.kts +++ b/edc-extensions/hashicorp-vault/build.gradle.kts @@ -9,7 +9,7 @@ dependencies { implementation(edc.junit) implementation(libs.bouncyCastle.bcpkix) implementation(libs.okhttp) - implementation("org.testcontainers:junit-jupiter:1.17.6") + implementation("org.testcontainers:junit-jupiter:1.18.0") implementation("org.testcontainers:vault:1.17.6") testImplementation(libs.mockito.inline) } From bdd8482c9b5742b1dd58dc9acc869400aca6d573 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 07:57:25 +0200 Subject: [PATCH 076/132] chore(deps): bump com.bmuschko.docker-remote-api from 9.2.1 to 9.3.1 (#222) Bumps com.bmuschko.docker-remote-api from 9.2.1 to 9.3.1. --- updated-dependencies: - dependency-name: com.bmuschko.docker-remote-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 33408a8b1..13dc385b0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { id("io.freefair.lombok") version "6.6.2" id("com.diffplug.spotless") version "6.15.0" id("com.github.johnrengelman.shadow") version "8.0.0" - id("com.bmuschko.docker-remote-api") version "9.2.1" + id("com.bmuschko.docker-remote-api") version "9.3.1" id("org.sonarqube") version "4.0.0.2929" } From 945ef0c27d7a5262925735af1042ad4eda72cc8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 07:58:20 +0200 Subject: [PATCH 077/132] chore(deps): bump org.testcontainers:vault from 1.17.6 to 1.18.0 (#223) Bumps [org.testcontainers:vault](https://github.com/testcontainers/testcontainers-java) from 1.17.6 to 1.18.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.17.6...1.18.0) --- updated-dependencies: - dependency-name: org.testcontainers:vault dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> --- edc-extensions/hashicorp-vault/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-extensions/hashicorp-vault/build.gradle.kts b/edc-extensions/hashicorp-vault/build.gradle.kts index 8433e74a3..1fc98b31b 100644 --- a/edc-extensions/hashicorp-vault/build.gradle.kts +++ b/edc-extensions/hashicorp-vault/build.gradle.kts @@ -9,7 +9,7 @@ dependencies { implementation(edc.junit) implementation(libs.bouncyCastle.bcpkix) implementation(libs.okhttp) + implementation("org.testcontainers:vault:1.18.0") implementation("org.testcontainers:junit-jupiter:1.18.0") - implementation("org.testcontainers:vault:1.17.6") testImplementation(libs.mockito.inline) } From fd241d069609e421060f79811cf8df34553f3894 Mon Sep 17 00:00:00 2001 From: "Sascha Isele (ZF Friedrichshafen AG)" <127207440+saschaisele-zf@users.noreply.github.com> Date: Mon, 17 Apr 2023 10:16:13 +0200 Subject: [PATCH 078/132] docs(control-plane-adapter): improve documentation on how to use the control-plane adapter extension (#210) --- edc-extensions/control-plane-adapter/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/edc-extensions/control-plane-adapter/README.md b/edc-extensions/control-plane-adapter/README.md index fe9d4787c..5f2d0d890 100644 --- a/edc-extensions/control-plane-adapter/README.md +++ b/edc-extensions/control-plane-adapter/README.md @@ -39,9 +39,17 @@ To run CP-Adapter in "PERSISTENT" mode, You need to create a proper tables with 1. Client sends a GET request with two parameters: assetId and the url of the provider controlplane: ```plain - /adapter/asset/sync/{assetId}?providerUrl={providerUrl} + {controlplaneUrl}:{web.http.management.port}/{web.http.management.path}/adapter/asset/sync/{assetId}?providerUrl={providerUrl} ``` + | Name | Description | + |----------------------------|----------------------------------------------------------------------------------| + | `controlplaneUrl` | The URL where the control plane of the consumer connector is available | + | `web.http.management.port` | Port of the management API provided by the control plane | + | `web.http.management.path` | Path of the management API provided by the control plane | + | `assetId` | ID of the wanted asset | + | `providerUrl` | URL pointing to the `data` endpoint of the IDS context of the provider connector | + The example ULR could be: ```plain From ec424a847d950e3ebb17e984e13b26ca26e2837a Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Mon, 17 Apr 2023 10:49:41 +0200 Subject: [PATCH 079/132] feature: create in-mem helm chart (#219) * feature: create the tractusx-connector-memory chart * pr remarks * pr remarks * increase waiting for negotiation, sometimes takes longer then 2 seconds * Apply suggestions from code review Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * pr remarks * Update charts/tractusx-connector-memory/templates/deployment-runtime.yaml Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --------- Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- .../actions/publish-docker-image/action.yml | 2 - .../actions/run-deployment-test/action.yml | 103 ++++++ .github/workflows/build.yaml | 11 +- .github/workflows/business-tests.yaml | 1 - .github/workflows/deploy-test-secrets | 51 +++ .github/workflows/deployment-test.yaml | 67 ++++ .github/workflows/publish-docker.yaml | 2 +- .github/workflows/trivy.yml | 25 +- .github/workflows/veracode.yaml | 44 +-- CHANGELOG.md | 38 ++- README.md | 30 +- charts/tractusx-connector-memory/.helmignore | 23 ++ charts/tractusx-connector-memory/Chart.yaml | 45 +++ charts/tractusx-connector-memory/README.md | 241 ++++++++++++++ .../README.md.gotmpl | 26 ++ charts/tractusx-connector-memory/example.yaml | 65 ++++ .../templates/NOTES.txt | 22 ++ .../templates/_helpers.tpl | 157 +++++++++ .../templates/configmap-runtime.yaml | 33 ++ .../templates/deployment-runtime.yaml | 302 +++++++++++++++++ .../templates/hpa-runtime.yaml | 29 ++ .../templates/ingress-runtime.yaml | 77 +++++ .../templates/service-runtime.yaml | 59 ++++ .../templates/serviceaccount.yaml | 16 + .../templates/tests/test-readiness.yaml | 15 + charts/tractusx-connector-memory/values.yaml | 313 ++++++++++++++++++ .../templates/deployment-controlplane.yaml | 2 +- docs/README.md | 12 +- docs/migration/Version_0.3.1_0.3.2.md | 2 +- edc-controlplane/build.gradle.kts | 3 +- .../README.md | 109 +++--- .../build.gradle.kts | 11 +- .../notice.md | 4 +- .../src/main/docker/Dockerfile | 22 +- .../edc/vault/memory/InMemoryVault.java | 53 +++ .../vault/memory/VaultMemoryExtension.java | 54 +++ ...rg.eclipse.edc.spi.system.ServiceExtension | 21 ++ .../edc/vault/memory/InMemoryVaultTest.java | 56 ++++ .../memory/VaultMemoryExtensionTest.java | 52 +++ .../supporting-infrastructure/values.yaml | 13 - .../tractusx/edc/tests/data/Negotiation.java | 2 +- .../main/resources/helm/omejdn/.helmignore | 23 ++ .../src/main/resources/helm/omejdn/Chart.yaml | 25 ++ .../src/main/resources/helm/omejdn/README.md | 21 ++ .../helm/omejdn/templates/_helpers.tpl | 62 ++++ .../helm/omejdn/templates/configmap.yaml | 73 ++++ .../helm/omejdn/templates/deployment.yaml | 149 +++++++++ .../resources/helm/omejdn/templates/hpa.yaml | 28 ++ .../omejdn/templates/imagepullsecret.yaml | 13 + .../helm/omejdn/templates/service.yaml | 15 + .../helm/omejdn/templates/serviceaccount.yaml | 12 + .../main/resources/helm/omejdn/values.yaml | 91 +++++ .../helm/test-infrastructure/.gitignore | 4 + .../helm/test-infrastructure/.helmignore | 24 ++ .../helm/test-infrastructure/Chart.yaml | 54 +++ .../helm/test-infrastructure/README.md | 54 +++ .../helm/test-infrastructure/values.yaml | 185 +++++++++++ settings.gradle.kts | 36 +- 58 files changed, 2883 insertions(+), 199 deletions(-) create mode 100644 .github/actions/run-deployment-test/action.yml create mode 100644 .github/workflows/deploy-test-secrets create mode 100644 .github/workflows/deployment-test.yaml create mode 100644 charts/tractusx-connector-memory/.helmignore create mode 100644 charts/tractusx-connector-memory/Chart.yaml create mode 100644 charts/tractusx-connector-memory/README.md create mode 100644 charts/tractusx-connector-memory/README.md.gotmpl create mode 100644 charts/tractusx-connector-memory/example.yaml create mode 100644 charts/tractusx-connector-memory/templates/NOTES.txt create mode 100644 charts/tractusx-connector-memory/templates/_helpers.tpl create mode 100644 charts/tractusx-connector-memory/templates/configmap-runtime.yaml create mode 100644 charts/tractusx-connector-memory/templates/deployment-runtime.yaml create mode 100644 charts/tractusx-connector-memory/templates/hpa-runtime.yaml create mode 100644 charts/tractusx-connector-memory/templates/ingress-runtime.yaml create mode 100644 charts/tractusx-connector-memory/templates/service-runtime.yaml create mode 100644 charts/tractusx-connector-memory/templates/serviceaccount.yaml create mode 100644 charts/tractusx-connector-memory/templates/tests/test-readiness.yaml create mode 100644 charts/tractusx-connector-memory/values.yaml rename edc-controlplane/{edc-controlplane-memory => edc-runtime-memory}/README.md (54%) rename edc-controlplane/{edc-controlplane-memory => edc-runtime-memory}/build.gradle.kts (77%) rename edc-controlplane/{edc-controlplane-memory => edc-runtime-memory}/notice.md (90%) rename edc-controlplane/{edc-controlplane-memory => edc-runtime-memory}/src/main/docker/Dockerfile (61%) create mode 100644 edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVault.java create mode 100644 edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java create mode 100644 edc-controlplane/edc-runtime-memory/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension create mode 100644 edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVaultTest.java create mode 100644 edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/README.md create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 600383f81..206e13d4c 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -48,7 +48,6 @@ runs: # Login to DockerHub ##################### - name: DockerHub login - if: github.event_name != 'pull_request' uses: docker/login-action@v2 with: username: ${{ inputs.docker_user }} @@ -108,7 +107,6 @@ runs: # https://github.com/peter-evans/dockerhub-description ############################### - name: Update Docker Hub description - if: github.event_name != 'pull_request' uses: peter-evans/dockerhub-description@v3 with: readme-filepath: ${{ inputs.rootDir }}/notice.md diff --git a/.github/actions/run-deployment-test/action.yml b/.github/actions/run-deployment-test/action.yml new file mode 100644 index 000000000..ed720b4be --- /dev/null +++ b/.github/actions/run-deployment-test/action.yml @@ -0,0 +1,103 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +name: "Run Deployment Test" +description: "Build and publish a Docker Image to DockerHub" +inputs: + imagename: + required: true + description: "name of the docker image, e.g. edc-runtime-memory" + + image_tag: + required: false + default: "latest" + description: "docker image tag, defaults to 'latest'" + + helm_command: + required: true + description: "command which is executed to install the chart. must also include verification commands, such as 'helm test'" + + rootDir: + required: true + description: "The directory that contains the docker file, e.g. edc-controlplane/edc-runtime-memory" + +runs: + using: "composite" + steps: + - name: Checkout + uses: actions/checkout@v3.3.0 + + - name: Cache ContainerD Image Layers + uses: actions/cache@v3 + with: + path: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs + key: ${{ runner.os }}-io.containerd.snapshotter.v1.overlayfs + + - name: Set up JDK 11 + uses: actions/setup-java@v3.11.0 + with: + java-version: '11' + distribution: 'temurin' + cache: 'gradle' + + - name: Build docker images + shell: bash + run: |- + ./gradlew -p ${{ inputs.rootDir }} dockerize + + - name: Setup Helm + uses: azure/setup-helm@v3.5 + with: + version: v3.8.1 + + - name: Setup Kubectl + uses: azure/setup-kubectl@v3.2 + + - name: Create k8s Kind Cluster + uses: helm/kind-action@v1.5.0 + + - name: Load images into KinD + shell: bash + run: | + kind get clusters | xargs -n1 kind load docker-image ${{ inputs.imagename }}:${{ inputs.image_tag }} --name + + ################################################### + # Install the test infrastructure + ################################################### + - name: Install Infrastructure + shell: bash + run: |- + helm install infra edc-tests/deployment/src/main/resources/helm/test-infrastructure \ + --wait-for-jobs --timeout=30s --dependency-update + + - name: Install Runtime + shell: bash + run: ${{ inputs.helm_command }} + + + ################# + ### Tear Down ### + ################# + - name: Destroy the kind cluster + if: always() + shell: bash + run: >- + kind get clusters | xargs -n1 kind delete cluster --name \ No newline at end of file diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 76d0e01c5..2c2dda9c2 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -55,6 +55,7 @@ jobs: SONAR_TOKEN: ${{ steps.secret-presence.outputs.SONAR_TOKEN }} GPG_PRIVATE_KEY: ${{ steps.secret-presence.outputs.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ steps.secret-presence.outputs.GPG_PASSPHRASE }} + DOCKER_HUB_TOKEN: ${{ steps.secret-presence.outputs.DOCKER_HUB_TOKEN }} steps: - name: Check whether secrets exist id: secret-presence @@ -62,6 +63,7 @@ jobs: [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "::set-output name=SONAR_TOKEN::true" [ ! -z "${{ secrets.GPG_PRIVATE_KEY }}" ] && echo "::set-output name=GPG_PRIVATE_KEY::true" [ ! -z "${{ secrets.GPG_PASSPHRASE }}" ] && echo "::set-output name=GPG_PASSPHRASE::true" + [ ! -z "${{ secrets.DOCKER_HUB_TOKEN }}" ] && echo "::set-output name=DOCKER_HUB_TOKEN::true" exit 0 build-extensions: @@ -89,11 +91,13 @@ jobs: name: "Create Docker Images for the ControlPlane" runs-on: ubuntu-latest needs: [ secret-presence ] + if: | + needs.secret-presence.outputs.DOCKER_HUB_TOKEN strategy: fail-fast: false matrix: name: - - edc-controlplane-memory + - edc-runtime-memory - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault @@ -110,8 +114,11 @@ jobs: docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} build-dataplane: + name: "Create Docker Images for the DataPlane" runs-on: ubuntu-latest needs: [ secret-presence ] + if: | + needs.secret-presence.outputs.DOCKER_HUB_TOKEN strategy: fail-fast: false matrix: @@ -135,7 +142,7 @@ jobs: permissions: contents: read packages: write - needs: [ secret-presence, build-controlplane, build-dataplane, build-extensions ] + needs: [ secret-presence, build-extensions ] # do not run on PR branches, do not run on releases if: | diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 97a3044fb..39caaadb1 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -166,7 +166,6 @@ jobs: sleep 5s # Wait for supporting infrastructure to become ready (control-/data-plane, backend service) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=backend --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=backend --tail 500 && exit 1 ) kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=idsdaps --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=idsdaps --tail 500 && exit 1 ) kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=vault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=vault --tail 500 && exit 1 ) kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokrates-postgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokrates-postgresql --tail 500 && exit 1 ) diff --git a/.github/workflows/deploy-test-secrets b/.github/workflows/deploy-test-secrets new file mode 100644 index 000000000..28596b459 --- /dev/null +++ b/.github/workflows/deploy-test-secrets @@ -0,0 +1,51 @@ +daps-key:-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM +wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ +FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 +8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ +ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE +sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc +RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z +d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm +hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm +cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh +FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F +MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e +uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb +ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 +z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 +h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ +vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB +8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 +hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 +dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 +Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 +IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT +3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr +0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 +u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B +AjWFbUiBCFOo+gpAFcQGrkOQHA== +-----END PRIVATE KEY-----;daps-crt:-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL +BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl +cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 +bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ +TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE +BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK +DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD +DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw +78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa +llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV +grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 +PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 +ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT +MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH +LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd +iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E +28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 +S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r +uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB +UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml new file mode 100644 index 000000000..7d38b24ac --- /dev/null +++ b/.github/workflows/deployment-test.yaml @@ -0,0 +1,67 @@ +# +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +name: "Deployment Tests" + +on: + push: + branches: + - main + - develop + tags: + - '[0-9]+.[0-9]+.[0-9]+' + release: + types: + - published + pull_request: + paths-ignore: + - 'docs/**' + - '**/*.md' + branches: + - '*' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + deployment-test-memory: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/run-deployment-test + name: "Run deployment test using KinD and Helm" + with: + imagename: edc-runtime-memory + rootDir: edc-controlplane/edc-runtime-memory + helm_command: |- + helm install tx-inmem charts/tractusx-connector-memory \ + -f charts/tractusx-connector-memory/example.yaml \ + --set vault.secrets="$(cat ./.github/workflows/deploy-test-secrets)" \ + --wait-for-jobs --timeout=120s + + # wait for the pod to become ready + kubectl rollout status deployment tx-inmem + + # execute the helm test + helm test tx-inmem diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index e2a7a5384..794d15061 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -42,7 +42,7 @@ jobs: fail-fast: false matrix: name: - - edc-controlplane-memory + - edc-runtime-memory - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 714ed4c74..c315e8a07 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -6,7 +6,7 @@ on: - cron: "0 0 * * *" workflow_dispatch: workflow_run: - workflows: ["Build"] + workflows: [ "Build" ] branches: - main - releases @@ -24,8 +24,7 @@ jobs: outputs: value: ${{ steps.git-sha7.outputs.SHA7 }} steps: - - - name: Resolve git 7-chars sha + - name: Resolve git 7-chars sha id: git-sha7 run: | echo "::set-output name=SHA7::${GITHUB_SHA::7}" @@ -37,11 +36,9 @@ jobs: contents: read security-events: write steps: - - - name: Checkout repository + - name: Checkout repository uses: actions/checkout@v3.3.0 - - - name: Run Trivy vulnerability scanner in repo mode + - name: Run Trivy vulnerability scanner in repo mode uses: aquasecurity/trivy-action@master with: scan-type: "config" @@ -51,8 +48,7 @@ jobs: format: "sarif" output: "trivy-results-config.sarif" severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab + - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 if: always() with: @@ -69,18 +65,16 @@ jobs: fail-fast: false # continue scanning other images although if the other has been vulnerable matrix: image: - - edc-controlplane-memory + - edc-runtime-memory - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault - edc-dataplane-azure-vault - edc-dataplane-hashicorp-vault steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v3.3.0 - - - name: Run Trivy vulnerability scanner + - name: Run Trivy vulnerability scanner if: always() uses: aquasecurity/trivy-action@master with: @@ -90,8 +84,7 @@ jobs: exit-code: "1" severity: "CRITICAL,HIGH" timeout: "10m0s" - - - name: Upload Trivy scan results to GitHub Security tab + - name: Upload Trivy scan results to GitHub Security tab if: always() uses: github/codeql-action/upload-sarif@v2 with: diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index a58d3fa4d..bba9df1b5 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -13,8 +13,7 @@ jobs: ORG_VERACODE_API_ID: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_ID }} ORG_VERACODE_API_KEY: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_KEY }} steps: - - - name: Check whether secrets exist + - name: Check whether secrets exist id: secret-presence run: | [ ! -z "${{ secrets.ORG_VERACODE_API_ID }}" ] && echo "::set-output name=ORG_VERACODE_API_ID::true" @@ -24,20 +23,17 @@ jobs: verify-formatting: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v3.3.0 with: fetch-depth: 0 - - - name: Set up JDK 11 + - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: java-version: '17' distribution: 'temurin' cache: 'gradle' - - - name: Verify proper formatting + - name: Verify proper formatting run: ./gradlew spotlessCheck build-controlplane: @@ -49,36 +45,31 @@ jobs: fail-fast: false matrix: name: - - edc-controlplane-memory + - edc-runtime-memory - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault steps: # Set-Up - - - name: Checkout + - name: Checkout uses: actions/checkout@v3.3.0 - - - name: Set up JDK 11 + - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: java-version: '17' distribution: 'temurin' cache: 'gradle' # Build - - - name: Build Controlplane + - name: Build Controlplane run: |- ./gradlew -p edc-controlplane/${{ matrix.name }} shadowJar env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: Tar gzip files for veracode upload + - name: Tar gzip files for veracode upload run: |- tar -czvf edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.tar.gz edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - - - name: Veracode Upload And Scan + - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY @@ -104,30 +95,25 @@ jobs: - edc-dataplane-hashicorp-vault steps: # Set-Up - - - name: Checkout + - name: Checkout uses: actions/checkout@v3.3.0 - - - name: Set up JDK 11 + - name: Set up JDK 11 uses: actions/setup-java@v3.11.0 with: java-version: '17' distribution: 'temurin' cache: 'gradle' # Build - - - name: Build Dataplane + - name: Build Dataplane run: |- ./gradlew -p edc-dataplane/${{ matrix.name }} shadowJar env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: Tar gzip files for veracode upload + - name: Tar gzip files for veracode upload run: |- tar -czvf edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.tar.gz edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - - - name: Veracode Upload And Scan + - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY diff --git a/CHANGELOG.md b/CHANGELOG.md index 79051eb6d..e84846211 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - update control plane docu (#623) - update postgresql version in Chart.yaml supporting-infrastructure (#622) - update link to edc logo in README.md (#612) -- update description of supporting infrastructure deployment (#616) +- update description of supporting infrastructure deployment (#616) ### Fixed @@ -84,7 +84,7 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - Bump alpine (#749) - Bump alpine (#750) - Bump alpine (#752) -- Bump alpine in /edc-controlplane/edc-controlplane-memory/src/main/docker (#753) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#753) - Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) - Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) - Bump s3 from 2.19.33 to 2.20.0 @@ -117,7 +117,7 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). - Bump s3 from 2.19.11 to 2.19.15 (#668) - Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) - Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) -- Bump alpine in /edc-controlplane/edc-controlplane-memory/src/main/docker (#659) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#659) - Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) - Bump alpine (#658) - Bump alpine (#661) @@ -171,7 +171,8 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). ## [0.1.1] - 2022-09-04 -**Important Note**: Please consolidate the migration documentation before updating your connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). +**Important Note**: Please consolidate the migration documentation before updating your +connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). ### Added @@ -184,7 +185,8 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). ### Fixed -- Connectors with Azure Vault extension are now starting again [link](https://github.com/eclipse-edc/Connector/issues/1892) +- Connectors with Azure Vault extension are now starting + again [link](https://github.com/eclipse-edc/Connector/issues/1892) ## [0.1.0] - 2022-08-19 @@ -193,11 +195,13 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Added -- Control-Plane extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) +- Control-Plane + extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) - run the EDC with multiple data planes at once - Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) - add data plane instances to the control plane by configuration -- Data-Plane extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) +- Data-Plane + extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) - transfer from and to AWS S3 buckets - Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) @@ -205,19 +209,27 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Changed - Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) -- EDC has been updated to version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - implications to the behavior of the connector have been covered in the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) +- EDC has been updated to + version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - + implications to the behavior of the connector have been covered in + the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) ### Fixed -- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) -- Deletion of Asset becomes impossible when Contract Negotiation exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) -- Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving + offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) +- Deletion of Asset becomes impossible when Contract Negotiation + exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) +- Deletion of Policy becomes impossible when Contract Definition + exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) ## [0.0.6] - 2022-07-29 ### Fixed -- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) library +- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath + issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) + library ## [0.0.5] - 2022-07-28 @@ -245,7 +257,7 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Fixed - [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 - contract offers max. + contract offers max. ### Removed diff --git a/README.md b/README.md index 566e42f5a..d1c6a0422 100644 --- a/README.md +++ b/README.md @@ -16,24 +16,25 @@ Please also refer to: ## About The Project -The project provides pre-built control- and data-plane [docker](https://www.docker.com/) images and [helm](https://helm.sh/) charts of the [Eclipse DataSpaceConnector Project](https://github.com/eclipse-edc/Connector). +The project provides pre-built control- and data-plane [docker](https://www.docker.com/) images +and [helm](https://helm.sh/) charts of +the [Eclipse DataSpaceConnector Project](https://github.com/eclipse-edc/Connector). ## Inventory -The eclipse data space connector is split up into Control-Plane and Data-Plane, whereas the Control-Plane functions as administration layer -and has responsibility of resource management, contract negotiation and administer data transfer. +The eclipse data space connector is split up into Control-Plane and Data-Plane, whereas the Control-Plane functions as +administration layer and has responsibility of resource management, contract negotiation and administer data transfer. The Data-Plane does the heavy lifting of transferring and receiving data streams. Depending on your environment there are different derivatives of the control-plane prepared: -- [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) with dependency onto - - [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) - [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) with dependency onto - [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) - [PostgreSQL 8.2 or newer](https://www.postgresql.org/) -- [edc-controlplane-postgresql-hashicorp-vault](edc-controlplane/edc-controlplane-postgresql-hashicorp-vault) with dependency onto +- [edc-controlplane-postgresql-hashicorp-vault](edc-controlplane/edc-controlplane-postgresql-hashicorp-vault) with + dependency onto - [Hashicorp Vault](https://www.vaultproject.io/) - -[PostgreSQL 8.2 or newer](https://www.postgresql.org/) + - [PostgreSQL 8.2 or newer](https://www.postgresql.org/) Derivatives of the Data-Plane can be found here @@ -42,6 +43,10 @@ Derivatives of the Data-Plane can be found here - [edc-dataplane-hashicorp-vault](edc-dataplane/edc-dataplane-hashicorp-vault) with dependency onto - [Hashicorp Vault](https://www.vaultproject.io/) +For testing/development purposes: + +- [edc-runtime-memory](edc-controlplane/edc-runtime-memory) + ## Getting Started ### Build @@ -54,15 +59,24 @@ Build Tractus-X EDC together with its Container Images ## License -Distributed under the Apache 2.0 License. See [LICENSE](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) for more information. +Distributed under the Apache 2.0 License. +See [LICENSE](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) for more information. + [contributors-shield]: https://img.shields.io/github/contributors/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge + [contributors-url]: https://github.com/eclipse-tractusx/tractusx-edc/graphs/contributors + [stars-shield]: https://img.shields.io/github/stars/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge + [stars-url]: https://github.com/eclipse-tractusx/tractusx-edc/stargazers + [license-shield]: https://img.shields.io/github/license/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge + [license-url]: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE + [release-shield]: https://img.shields.io/github/v/release/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge + [release-url]: https://github.com/eclipse-tractusx/tractusx-edc/releases diff --git a/charts/tractusx-connector-memory/.helmignore b/charts/tractusx-connector-memory/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/charts/tractusx-connector-memory/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/tractusx-connector-memory/Chart.yaml b/charts/tractusx-connector-memory/Chart.yaml new file mode 100644 index 000000000..42b139a55 --- /dev/null +++ b/charts/tractusx-connector-memory/Chart.yaml @@ -0,0 +1,45 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +apiVersion: v2 +name: tractusx-connector-memory +description: A Helm chart for Tractus-X Eclipse Data Space Connector based on memory +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.3.2 +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.3.2" +home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory +sources: + - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md new file mode 100644 index 000000000..1e37bc286 --- /dev/null +++ b/charts/tractusx-connector-memory/README.md @@ -0,0 +1,241 @@ +# tractusx-connector + +![Version: 0.3.2](https://img.shields.io/badge/Version-0.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.2](https://img.shields.io/badge/AppVersion-0.3.2-informational?style=flat-square) + +A Helm chart for Tractus-X Eclipse Data Space Connector + +**Homepage:** + +## TL;DR + +```shell +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/tractusx-connector --version 0.3.2 +``` + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|---------------------------------------------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| backendService.httpProxyTokenReceiverUrl | string | `""` | | +| runtime.affinity | object | `{}` | | +| runtime.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | +| runtime.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| runtime.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| runtime.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| runtime.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| runtime.debug.enabled | bool | `false` | | +| runtime.debug.port | int | `1044` | | +| runtime.debug.suspendOnStart | bool | `false` | | +| runtime.endpoints | object | `{"control":{"path":"/control","port":8083},"data":{"authKey":"","path":"/data","port":8081},"default":{"path":"/api","port":8080},"ids":{"path":"/api/v1/ids","port":8084},"metrics":{"path":"/metrics","port":9090},"observability":{"insecure":true,"path":"/observability","port":8085},"validation":{"path":"/validation","port":8082}}` | endpoints of the control plane | +| runtime.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | +| runtime.endpoints.control.path | string | `"/control"` | path for incoming api calls | +| runtime.endpoints.control.port | int | `8083` | port for incoming api calls | +| runtime.endpoints.data | object | `{"authKey":"","path":"/data","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | +| runtime.endpoints.data.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | +| runtime.endpoints.data.path | string | `"/data"` | path for incoming api calls | +| runtime.endpoints.data.port | int | `8081` | port for incoming api calls | +| runtime.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | +| runtime.endpoints.default.path | string | `"/api"` | path for incoming api calls | +| runtime.endpoints.default.port | int | `8080` | port for incoming api calls | +| runtime.endpoints.ids | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | +| runtime.endpoints.ids.path | string | `"/api/v1/ids"` | path for incoming api calls | +| runtime.endpoints.ids.port | int | `8084` | port for incoming api calls | +| runtime.endpoints.metrics | object | `{"path":"/metrics","port":9090}` | metrics api, used for application metrics, must not be internet facing | +| runtime.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | +| runtime.endpoints.metrics.port | int | `9090` | port for incoming api calls | +| runtime.endpoints.observability | object | `{"insecure":true,"path":"/observability","port":8085}` | observability api with unsecured access, must not be internet facing | +| runtime.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | +| runtime.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | +| runtime.endpoints.observability.port | int | `8085` | port for incoming API calls | +| runtime.endpoints.validation | object | `{"path":"/validation","port":8082}` | validation api, only used by the data plane and should not be added to any ingress | +| runtime.endpoints.validation.path | string | `"/validation"` | path for incoming api calls | +| runtime.endpoints.validation.port | int | `8082` | port for incoming api calls | +| runtime.env | object | `{}` | | +| runtime.envConfigMapNames | list | `[]` | | +| runtime.envSecretNames | list | `[]` | | +| runtime.envValueFrom | object | `{}` | | +| runtime.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| runtime.image.repository | string | `""` | Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically | +| runtime.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | +| runtime.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| runtime.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| runtime.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| runtime.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| runtime.ingresses[0].enabled | bool | `false` | | +| runtime.ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | +| runtime.ingresses[0].hostname | string | `"edc-control.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| runtime.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| runtime.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| runtime.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | +| runtime.ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | +| runtime.ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| runtime.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| runtime.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| runtime.ingresses[1].enabled | bool | `false` | | +| runtime.ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | +| runtime.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| runtime.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| runtime.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| runtime.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | +| runtime.initContainers | list | `[]` | | +| runtime.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | +| runtime.internationalDataSpaces.curator | string | `""` | | +| runtime.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | +| runtime.internationalDataSpaces.id | string | `"TXDC"` | | +| runtime.internationalDataSpaces.maintainer | string | `""` | | +| runtime.internationalDataSpaces.title | string | `""` | | +| runtime.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| runtime.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| runtime.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | +| runtime.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| runtime.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| runtime.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| runtime.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| runtime.nodeSelector | object | `{}` | | +| runtime.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | +| runtime.podAnnotations | object | `{}` | additional annotations for the pod | +| runtime.podLabels | object | `{}` | additional labels for the pod | +| runtime.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| runtime.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| runtime.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| runtime.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| runtime.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | +| runtime.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| runtime.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| runtime.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | +| runtime.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a readiness check every 10 seconds | +| runtime.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| runtime.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| runtime.replicaCount | int | `1` | | +| runtime.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | +| runtime.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | +| runtime.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| runtime.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| runtime.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | +| runtime.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| runtime.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | +| runtime.service.annotations | object | `{}` | | +| runtime.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| runtime.tolerations | list | `[]` | | +| runtime.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | +| runtime.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | +| runtime.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | +| customLabels | object | `{}` | | +| daps.clientId | string | `""` | | +| daps.paths.jwks | string | `"/jwks.json"` | | +| daps.paths.token | string | `"/token"` | | +| daps.url | string | `""` | | +| dataplane.affinity | object | `{}` | | +| dataplane.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | +| dataplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| dataplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| dataplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| dataplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| dataplane.aws.accessKeyId | string | `""` | | +| dataplane.aws.endpointOverride | string | `""` | | +| dataplane.aws.secretAccessKey | string | `""` | | +| dataplane.debug.enabled | bool | `false` | | +| dataplane.debug.port | int | `1044` | | +| dataplane.debug.suspendOnStart | bool | `false` | | +| dataplane.endpoints.control.path | string | `"/api/dataplane/control"` | | +| dataplane.endpoints.control.port | int | `8083` | | +| dataplane.endpoints.default.path | string | `"/api"` | | +| dataplane.endpoints.default.port | int | `8080` | | +| dataplane.endpoints.metrics.path | string | `"/metrics"` | | +| dataplane.endpoints.metrics.port | int | `9090` | | +| dataplane.endpoints.public.path | string | `"/api/public"` | | +| dataplane.endpoints.public.port | int | `8081` | | +| dataplane.endpoints.validation.path | string | `"/validation"` | | +| dataplane.endpoints.validation.port | int | `8082` | | +| dataplane.env | object | `{}` | | +| dataplane.envConfigMapNames | list | `[]` | | +| dataplane.envSecretNames | list | `[]` | | +| dataplane.envValueFrom | object | `{}` | | +| dataplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| dataplane.image.repository | string | `""` | Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically | +| dataplane.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | +| dataplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| dataplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| dataplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| dataplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| dataplane.ingresses[0].enabled | bool | `false` | | +| dataplane.ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | +| dataplane.ingresses[0].hostname | string | `"edc-data.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| dataplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| dataplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| dataplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | +| dataplane.initContainers | list | `[]` | | +| dataplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| dataplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| dataplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | +| dataplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| dataplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| dataplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| dataplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| dataplane.nodeSelector | object | `{}` | | +| dataplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | +| dataplane.podAnnotations | object | `{}` | additional annotations for the pod | +| dataplane.podLabels | object | `{}` | additional labels for the pod | +| dataplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| dataplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| dataplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| dataplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| dataplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | +| dataplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| dataplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| dataplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | +| dataplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| dataplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| dataplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| dataplane.replicaCount | int | `1` | | +| dataplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | +| dataplane.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | +| dataplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| dataplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| dataplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | +| dataplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| dataplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | +| dataplane.service.port | int | `80` | | +| dataplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| dataplane.tolerations | list | `[]` | | +| dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | +| dataplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | +| dataplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | +| fullnameOverride | string | `""` | | +| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| nameOverride | string | `""` | | +| postgresql.enabled | bool | `false` | | +| postgresql.jdbcUrl | string | `""` | | +| postgresql.password | string | `""` | | +| postgresql.username | string | `""` | | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.imagePullSecrets | list | `[]` | Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| serviceAccount.name | string | `""` | | +| vault.azure.certificate | string | `nil` | | +| vault.azure.client | string | `""` | | +| vault.azure.enabled | bool | `false` | | +| vault.azure.name | string | `""` | | +| vault.azure.secret | string | `nil` | | +| vault.azure.tenant | string | `""` | | +| vault.hashicorp.enabled | bool | `false` | | +| vault.hashicorp.healthCheck.enabled | bool | `true` | | +| vault.hashicorp.healthCheck.standbyOk | bool | `true` | | +| vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | +| vault.hashicorp.paths.secret | string | `"/v1/secret"` | | +| vault.hashicorp.timeout | int | `30` | | +| vault.hashicorp.token | string | `""` | | +| vault.hashicorp.url | string | `""` | | +| vault.secretNames.dapsPrivateKey | string | `"daps-private-key"` | | +| vault.secretNames.dapsPublicKey | string | `"daps-public-key"` | | +| vault.secretNames.transferProxyTokenEncryptionAesKey | string | `"transfer-proxy-token-encryption-aes-key"` | | +| vault.secretNames.transferProxyTokenSignerPrivateKey | string | `"transfer-proxy-token-signer-private-key"` | | +| vault.secretNames.transferProxyTokenSignerPublicKey | string | `"transfer-proxy-token-signer-public-key"` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/tractusx-connector-memory/README.md.gotmpl b/charts/tractusx-connector-memory/README.md.gotmpl new file mode 100644 index 000000000..b1671f5a2 --- /dev/null +++ b/charts/tractusx-connector-memory/README.md.gotmpl @@ -0,0 +1,26 @@ +{{ template "chart.header" . }} + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +## TL;DR + +```shell +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/tractusx-connector --version {{ .Version }} +``` + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/tractusx-connector-memory/example.yaml b/charts/tractusx-connector-memory/example.yaml new file mode 100644 index 000000000..57d12b039 --- /dev/null +++ b/charts/tractusx-connector-memory/example.yaml @@ -0,0 +1,65 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +## This file can be used to verify that the chart is working properly. It provides an exemplary configuration +## that is intended to be used with the supporting infrastructure. +## 1. install DAPS: +## helm install infrastructure edc-tests/deployment/src/main/resources/helm/test-infrastructure \ ─╯ +## --wait-for-jobs +## +## 2. install in-mem runtime. Note that the key and crt must match exactly the DAPS setup, c.f. edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml +## export DAPSKEY="" +## export DAPSCRT="" +## export YOUR_VAULT_SECRETS="daps-key:$DAPSKEY;daps-crt:$DAPSCRT" +## helm install trudy charts/tractusx-connector-memory -f charts/tractusx-connector-memory/example.yaml --set vault.secrets=$YOUR_VAULT_SECRETS + +fullnameOverride: tx-inmem +runtime: + service: + type: NodePort + endpoints: + data: + authKey: password + image: + pullPolicy: Never + tag: "latest" + repository: "edc-runtime-memory" + securityContext: + # avoids some errors in the log: cannot write temp files of large multipart requests when R/O + readOnlyRootFilesystem: false + +vault: + secretNames: + transferProxyTokenSignerPublicKey: daps-crt + transferProxyTokenSignerPrivateKey: daps-key + transferProxyTokenEncryptionAesKey: aes-keysc + dapsPrivateKey: daps-key + dapsPublicKey: daps-crt + + # this must be set through CLI args: --set vault.secrets=$YOUR_VAULT_SECRETS where YOUR_VAULT_SECRETS should + # be a string in the format "key1:secret1;key2:secret2;..." + secrets: + +daps: + url: "http://ids-daps:4567" + clientId: "E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65" + +backendService: + httpProxyTokenReceiverUrl: "http://backend:8080" diff --git a/charts/tractusx-connector-memory/templates/NOTES.txt b/charts/tractusx-connector-memory/templates/NOTES.txt new file mode 100644 index 000000000..cd49a4d15 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the runtime URL by running these commands: +{{ with index .Values.runtime.ingresses 0}} +{{- if .enabled }} +{{- range .paths }} + http{{ if .tls }}s{{ end }}://{{ .hostname }}{{ .path }} +{{- end }} +{{- else if contains "NodePort" $.Values.runtime.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ $.Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "txdc.fullname" $ }}) + export NODE_IP=$(kubectl get nodes --namespace {{ $.Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" $.Values.runtime.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "txdc.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "txdc.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ $.Values.runtime.service.port }} +{{- else if contains "ClusterIP" $.Values.runtime.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ $.Release.Namespace }} -l "app.kubernetes.io/name={{ include "txdc.name" $ }},app.kubernetes.io/instance={{ $.Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ $.Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ $.Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector-memory/templates/_helpers.tpl b/charts/tractusx-connector-memory/templates/_helpers.tpl new file mode 100644 index 000000000..1b70bf13b --- /dev/null +++ b/charts/tractusx-connector-memory/templates/_helpers.tpl @@ -0,0 +1,157 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "txdc.name" -}} +{{- default .Chart.Name .Values.nameOverride | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "txdc.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "txdc.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Control Common labels +*/}} +{{- define "txdc.labels" -}} +helm.sh/chart: {{ include "txdc.chart" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Control Common labels +*/}} +{{- define "txdc.runtime.labels" -}} +helm.sh/chart: {{ include "txdc.chart" . }} +{{ include "txdc.runtime.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: edc-runtime +app.kubernetes.io/part-of: edc +{{- end }} + +{{/* +Control Selector labels +*/}} +{{- define "txdc.runtime.selectorLabels" -}} +app.kubernetes.io/name: {{ include "txdc.name" . }}-runtime +app.kubernetes.io/instance: {{ .Release.Name }}-runtime +{{- end }} + +{{/* +Data Selector labels +*/}} +{{- define "txdc.dataplane.selectorLabels" -}} +app.kubernetes.io/name: {{ include "txdc.name" . }}-dataplane +app.kubernetes.io/instance: {{ .Release.Name }}-dataplane +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "txdc.runtime.serviceaccount.name" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "txdc.fullname" . ) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Control IDS URL +*/}} +{{- define "txdc.runtime.url.ids" -}} +{{- if .Values.runtime.url.ids }}{{/* if ids api url has been specified explicitly */}} +{{- .Values.runtime.url.ids }} +{{- else }}{{/* else when ids api url has not been specified explicitly */}} +{{- with (index .Values.runtime.ingresses 0) }} +{{- if .enabled }}{{/* if ingress enabled */}} +{{- if .tls.enabled }}{{/* if TLS enabled */}} +{{- printf "https://%s" .hostname -}} +{{- else }}{{/* else when TLS not enabled */}} +{{- printf "http://%s" .hostname -}} +{{- end }}{{/* end if tls */}} +{{- else }}{{/* else when ingress not enabled */}} +{{- printf "http://%s-runtime:%v" ( include "txdc.fullname" $ ) $.Values.runtime.endpoints.ids.port -}} +{{- end }}{{/* end if ingress */}} +{{- end }}{{/* end with ingress */}} +{{- end }}{{/* end if .Values.runtime.url.ids */}} +{{- end }} + +{{/* +Observability URL +*/}} +{{- define "tdxc.runtime.url.readiness" -}} +{{- printf "http://%s-runtime:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.observability.port $.Values.runtime.endpoints.observability.path -}} +{{- end }} + +{{/* +Validation URL +*/}} +{{- define "txdc.runtime.url.validation" -}} +{{- printf "http://%s-runtime:%v%s/token" ( include "txdc.fullname" $ ) $.Values.runtime.endpoints.validation.port $.Values.runtime.endpoints.validation.path -}} +{{- end }} + +{{/* +Data Control URL +*/}} +{{- define "txdc.dataplane.url.control" -}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.runtime.endpoints.control.port .Values.runtime.endpoints.control.path -}} +{{- end }} + +{{/* +Data Public URL +*/}} +{{- define "txdc.dataplane.url.public" -}} +{{- if .Values.runtime.url.public }}{{/* if public api url has been specified explicitly */}} +{{- .Values.runtime.url.public }} +{{- else }}{{/* else when public api url has not been specified explicitly */}} +{{- with (index .Values.runtime.ingresses 0) }} +{{- if .enabled }}{{/* if ingress enabled */}} +{{- if .tls.enabled }}{{/* if TLS enabled */}} +{{- printf "https://%s%s" .hostname $.Values.runtime.endpoints.public.path -}} +{{- else }}{{/* else when TLS not enabled */}} +{{- printf "http://%s%s" .hostname $.Values.runtime.endpoints.public.path -}} +{{- end }}{{/* end if tls */}} +{{- else }}{{/* else when ingress not enabled */}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.public.port $.Values.runtime.endpoints.public.path -}} +{{- end }}{{/* end if ingress */}} +{{- end }}{{/* end with ingress */}} +{{- end }}{{/* end if .Values.dataplane.url.public */}} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "txdc.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "txdc.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector-memory/templates/configmap-runtime.yaml b/charts/tractusx-connector-memory/templates/configmap-runtime.yaml new file mode 100644 index 000000000..8b6067e06 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/configmap-runtime.yaml @@ -0,0 +1,33 @@ +# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # 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 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "txdc.fullname" . }}-runtime + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} +data: + logging.properties: |- + {{- .Values.runtime.logging | nindent 4 }} diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml new file mode 100644 index 000000000..04386678c --- /dev/null +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -0,0 +1,302 @@ +# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # 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 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "txdc.fullname" . }} + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} +spec: + {{- if not .Values.runtime.autoscaling.enabled }} + replicas: {{ .Values.runtime.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "txdc.runtime.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.runtime.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "txdc.runtime.selectorLabels" . | nindent 8 }} + {{- with .Values.runtime.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "txdc.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.runtime.podSecurityContext | nindent 8 }} + initContainers: + {{- toYaml .Values.runtime.initContainers | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.runtime.securityContext | nindent 12 }} + # either use the specified image, or use the default one + {{- if .Values.runtime.image.repository }} + image: "{{ .Values.runtime.image.repository }}:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" + {{- else }} + image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-runtime-memory:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" + {{- end }} + + imagePullPolicy: {{ .Values.runtime.image.pullPolicy }} + ports: + {{- range $key,$value := .Values.runtime.endpoints }} + - name: {{ $key }} + containerPort: {{ $value.port }} + protocol: TCP + {{- end }} + {{- if .Values.runtime.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.runtime.endpoints.observability.path }}/check/liveness + port: {{ .Values.runtime.endpoints.observability.port }} + initialDelaySeconds: {{ .Values.runtime.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.runtime.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.runtime.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.runtime.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.runtime.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.runtime.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.runtime.endpoints.observability.path }}/check/readiness + port: {{ .Values.runtime.endpoints.observability.port }} + initialDelaySeconds: {{ .Values.runtime.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.runtime.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.runtime.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.runtime.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.runtime.readinessProbe.successThreshold }} + {{- end }} + resources: + {{- toYaml .Values.runtime.resources | nindent 12 }} + env: + {{- if .Values.runtime.debug.enabled }} + - name: "JAVA_TOOL_OPTIONS" + {{- if and .Values.runtime.debug.enabled .Values.runtime.debug.suspendOnStart }} + value: >- + {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%v" .Values.runtime.debug.port }} + {{- else }} + value: >- + {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=%v" .Values.runtime.debug.port }} + {{- end }} + {{- end }} + + ######################## + ## DAPS CONFIGURATION ## + ######################## + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/iam/oauth2/oauth2-core + - name: EDC_OAUTH_CLIENT_ID + value: {{ .Values.daps.clientId | required ".Values.daps.clientId is required" | quote }} + - name: EDC_OAUTH_PROVIDER_JWKS_URL + value: {{ printf "%s%s" .Values.daps.url .Values.daps.paths.jwks }} + - name: EDC_OAUTH_TOKEN_URL + value: {{ printf "%s%s" .Values.daps.url .Values.daps.paths.token }} + - name: EDC_OAUTH_PRIVATE_KEY_ALIAS + value: {{ .Values.vault.secretNames.dapsPrivateKey | required ".Values.vault.secretNames.dapsPrivateKey is required" | quote }} + - name: EDC_OAUTH_PUBLIC_KEY_ALIAS + value: {{ .Values.vault.secretNames.dapsPublicKey | required ".Values.vault.secretNames.dapsPublicKey is required" | quote }} + + ####### + # API # + ####### + - name: "EDC_API_AUTH_KEY" + value: {{ .Values.runtime.endpoints.data.authKey | required ".Values.runtime.endpoints.data.authKey is required" | quote }} + - name: "WEB_HTTP_DEFAULT_PORT" + value: {{ .Values.runtime.endpoints.default.port | quote }} + - name: "WEB_HTTP_DEFAULT_PATH" + value: {{ .Values.runtime.endpoints.default.path | quote }} + {{- if or (eq (substr 0 3 .Values.runtime.image.tag) "0.1") (eq (substr 0 3 .Values.runtime.image.tag) "0.2") }} + # WEB_HTTP_DATA_PORT is renamed to WEB_HTTP_MANAGEMENT_PORT from version 0.2.1 and newer + # we will keep both settings for downward capabilities + - name: "WEB_HTTP_DATA_PORT" + value: {{ .Values.runtime.endpoints.data.port | quote }} + # WEB_HTTP_DATA_PATH is renamed to WEB_HTTP_MANAGEMENT_PATH from version 0.2.1 and newer + # we will keep both settings for downward capabilities + - name: "WEB_HTTP_DATA_PATH" + value: {{ .Values.runtime.endpoints.data.path | quote }} + {{- else }} + - name: "WEB_HTTP_MANAGEMENT_PORT" + value: {{ .Values.runtime.endpoints.data.port | quote }} + - name: "WEB_HTTP_MANAGEMENT_PATH" + value: {{ .Values.runtime.endpoints.data.path | quote }} + {{- end }} + - name: "WEB_HTTP_VALIDATION_PORT" + value: {{ .Values.runtime.endpoints.validation.port | quote }} + - name: "WEB_HTTP_VALIDATION_PATH" + value: {{ .Values.runtime.endpoints.validation.path | quote }} + - name: "WEB_HTTP_CONTROL_PORT" + value: {{ .Values.runtime.endpoints.control.port | quote }} + - name: "WEB_HTTP_CONTROL_PATH" + value: {{ .Values.runtime.endpoints.control.path | quote }} + - name: "WEB_HTTP_IDS_PORT" + value: {{ .Values.runtime.endpoints.ids.port | quote }} + - name: "WEB_HTTP_IDS_PATH" + value: {{ .Values.runtime.endpoints.ids.path | quote }} + - name: "WEB_HTTP_OBSERVABILITY_PORT" + value: {{ .Values.runtime.endpoints.observability.port | quote}} + - name: "WEB_HTTP_OBSERVABILITY_PATH" + value: {{ .Values.runtime.endpoints.observability.path | quote}} + - name: "TRACTUSX_API_OBSERVABILITY_ALLOW-INSECURE" + value: {{ .Values.runtime.endpoints.observability.insecure | quote }} + - name: "WEB_HTTP_PUBLIC_PORT" + value: {{ .Values.runtime.endpoints.public.port | quote }} + - name: "WEB_HTTP_PUBLIC_PATH" + value: {{ .Values.runtime.endpoints.public.path | quote }} + - name: "EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT" + value: {{ include "txdc.runtime.url.validation" .}} + + ######### + ## IDS ## + ######### + - name: "IDS_WEBHOOK_ADDRESS" + value: {{ include "txdc.runtime.url.ids" . | quote }} + - name: "EDC_IDS_ENDPOINT" + value: {{ printf "%s%s" (include "txdc.runtime.url.ids" .) .Values.runtime.endpoints.ids.path | quote }} + - name: "EDC_IDS_ID" + value: {{ printf "urn:connector:%s" (lower .Values.runtime.internationalDataSpaces.id) | quote }} + - name: "EDC_IDS_DESCRIPTION" + value: {{ .Values.runtime.internationalDataSpaces.description | quote }} + - name: "EDC_IDS_TITLE" + value: {{ .Values.runtime.internationalDataSpaces.title | quote }} + - name: "EDC_IDS_MAINTAINER" + value: {{ .Values.runtime.internationalDataSpaces.maintainer | quote }} + - name: "EDC_IDS_CURATOR" + value: {{ .Values.runtime.internationalDataSpaces.curator | quote }} + - name: "EDC_IDS_CATALOG_ID" + value: {{ printf "urn:catalog:%s" (lower .Values.runtime.internationalDataSpaces.catalogId) | quote }} + - name: "EDC_OAUTH_PROVIDER_AUDIENCE" + value: "idsc:IDS_CONNECTORS_ALL" + - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" + value: {{ printf "%s%s%s" (include "txdc.runtime.url.ids" . ) .Values.runtime.endpoints.ids.path "/data" | quote }} + # this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older + - name: "EDC_IDS_ENDPOINT_AUDIENCE" + value: {{ printf "%s%s%s" (include "txdc.runtime.url.ids" . ) .Values.runtime.endpoints.ids.path "/data" | quote }} + + ################ + ## DATA PLANE ## + ################ + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/dataplane-selector-configuration + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" + value: {{ include "txdc.dataplane.url.control" . }}/transfer + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" + value: "HttpData,AmazonS3" + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_DESTINATIONTYPES" + value: "HttpProxy,AmazonS3" + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_PROPERTIES" + value: |- + {{ printf "{ \"publicApiUrl\": \"%s\" }" (include "txdc.dataplane.url.public" . ) }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/data-plane-transfer + - name: "EDC_TRANSFER_PROXY_ENDPOINT" + value: {{ include "txdc.dataplane.url.public" . }} + - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenSignerPrivateKey | quote }} + - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/http-receiver + - name: "EDC_RECEIVER_HTTP_ENDPOINT" + value: {{ .Values.backendService.httpProxyTokenReceiverUrl | required ".Values.backendService.httpProxyTokenReceiverUrl is required" | quote }} + + ########### + ## VAULT ## + ########### + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault + - name: "SECRETS" + value: {{ .Values.vault.secrets | quote}} + + ##################### + ## DATA ENCRYPTION ## + ##################### + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/data-encryption + - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} + - name: "EDC_DATA_ENCRYPTION_ALGORITHM" + value: "AES" + + ########################### + ## AAS WRAPPER EXTENSION ## + ########################### + - name: "EDC_CP_ADAPTER_CACHE_CATALOG_EXPIRE_AFTER" + value: "0" + - name: "EDC_CP_ADAPTER_REUSE_CONTRACT_AGREEMENT" + value: "0" + + ###################################### + ## Additional environment variables ## + ###################################### + {{- range $key, $value := .Values.runtime.envValueFrom }} + - name: {{ $key | quote }} + valueFrom: + {{- tpl (toYaml $value) $ | nindent 16 }} + {{- end }} + {{- range $key, $value := .Values.runtime.env }} + - name: {{ $key | quote }} + value: {{ $value | quote }} + {{- end }} + {{- if and (or .Values.runtime.envSecretNames .Values.runtime.envConfigMapNames) (or (gt (len .Values.runtime.envSecretNames) 0) (gt (len .Values.runtime.envConfigMapNames) 0)) }} + envFrom: + {{- range $value := .Values.runtime.envSecretNames }} + - secretRef: + name: {{ $value | quote }} + {{- end }} + {{- range $value := .Values.runtime.envConfigMapNames }} + - configMapRef: + name: {{ $value | quote }} + {{- end }} + {{- end }} + volumeMounts: + - name: "configuration" + mountPath: "/app/logging.properties" + subPath: "logging.properties" + volumes: + - name: "configuration" + configMap: + name: {{ include "txdc.fullname" . }}-runtime + items: + - key: "logging.properties" + path: "logging.properties" + {{- with .Values.runtime.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.runtime.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.runtime.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/tractusx-connector-memory/templates/hpa-runtime.yaml b/charts/tractusx-connector-memory/templates/hpa-runtime.yaml new file mode 100644 index 000000000..a373dfb63 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/hpa-runtime.yaml @@ -0,0 +1,29 @@ +{{- if .Values.runtime.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "txdc.fullname" . }}-runtime + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "txdc.fullname" . }}-runtime + minReplicas: {{ .Values.runtime.autoscaling.minReplicas }} + maxReplicas: {{ .Values.runtime.autoscaling.maxReplicas }} + metrics: + {{- if .Values.runtime.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.runtime.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.runtime.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.runtime.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/tractusx-connector-memory/templates/ingress-runtime.yaml b/charts/tractusx-connector-memory/templates/ingress-runtime.yaml new file mode 100644 index 000000000..06c6f5c68 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/ingress-runtime.yaml @@ -0,0 +1,77 @@ +{{- $fullName := include "txdc.fullname" . }} +{{- $controlLabels := include "txdc.runtime.labels" . | nindent 4 }} +{{- $controlEdcEndpoints := .Values.runtime.endpoints }} +{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} +{{- $namespace := .Release.Namespace }} + +{{- range .Values.runtime.ingresses }} +{{- if and .enabled .endpoints }} +{{- $controlIngressName := printf "%s-runtime-%s" $fullName .hostname }} +--- +{{- if semverCompare ">=1.19-0" $gitVersion }} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" $gitVersion }} +apiVersion: networking.k8s.io/v1beta1 +{{- else }} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $controlIngressName }} + namespace: {{ $namespace | default "default" | quote }} + labels: + {{- $controlLabels | nindent 2 }} + annotations: + {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} + {{- if not (hasKey .annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .annotations "kubernetes.io/ingress.class" .className}} + {{- end }} + {{- end }} + {{- if .certManager }} + {{- if .certManager.issuer }} + {{- $_ := set .annotations "cert-manager.io/issuer" .certManager.issuer}} + {{- end }} + {{- if .certManager.clusterIssuer }} + {{- $_ := set .annotations "cert-manager.io/cluster-issuer" .certManager.clusterIssuer}} + {{- end }} + {{- end }} + {{- with .annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .className (semverCompare ">=1.18-0" $gitVersion) }} + ingressClassName: {{ .className }} + {{- end }} + {{- if .hostname }} + {{- if .tls.enabled }} + tls: + - hosts: + - {{ .hostname }} + {{- if .tls.secretName }} + secretName: {{ .tls.secretName }} + {{- else }} + secretName: {{ $controlIngressName }}-tls + {{- end }} + {{- end }} + rules: + - host: {{ .hostname }} + http: + paths: + {{- $ingressEdcEndpoints := .endpoints }} + {{- range $name, $mapping := $controlEdcEndpoints }} + {{- if (has $name $ingressEdcEndpoints) }} + - path: {{ $mapping.path }} + pathType: Prefix + backend: + {{- if semverCompare ">=1.19-0" $gitVersion }} + service: + name: {{ $fullName }}-runtime + port: + number: {{ $mapping.port }} + {{- else }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} +{{- end }}{{- /* end: if .enabled */}} +{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/charts/tractusx-connector-memory/templates/service-runtime.yaml b/charts/tractusx-connector-memory/templates/service-runtime.yaml new file mode 100644 index 000000000..241e28885 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/service-runtime.yaml @@ -0,0 +1,59 @@ +# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # 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 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "txdc.fullname" . }}-runtime + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} +spec: + type: {{ .Values.runtime.service.type }} + ports: + - port: {{ .Values.runtime.endpoints.default.port }} + targetPort: default + protocol: TCP + name: default + - port: {{ .Values.runtime.endpoints.control.port }} + targetPort: control + protocol: TCP + name: control + - port: {{ .Values.runtime.endpoints.data.port }} + targetPort: data + protocol: TCP + name: data + - port: {{ .Values.runtime.endpoints.validation.port }} + targetPort: validation + protocol: TCP + name: validation + - port: {{ .Values.runtime.endpoints.ids.port }} + targetPort: ids + protocol: TCP + name: ids + - port: {{ .Values.runtime.endpoints.observability.port}} + targetPort: observability + protocol: TCP + name: observability + selector: + {{- include "txdc.runtime.selectorLabels" . | nindent 4 }} diff --git a/charts/tractusx-connector-memory/templates/serviceaccount.yaml b/charts/tractusx-connector-memory/templates/serviceaccount.yaml new file mode 100644 index 000000000..c650bcd68 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/serviceaccount.yaml @@ -0,0 +1,16 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "txdc.serviceAccountName" . }} + labels: + {{- include "txdc.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- with .Values.serviceAccount.imagePullSecrets }} +imagePullSecrets: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml new file mode 100644 index 000000000..d98493953 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "txdc.fullname" . }}-test-readiness" + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: curlimages/curl + command: ['curl'] + args: ['{{ include "tdxc.runtime.url.readiness" . }}'] + restartPolicy: Never diff --git a/charts/tractusx-connector-memory/values.yaml b/charts/tractusx-connector-memory/values.yaml new file mode 100644 index 000000000..66fa1b7fe --- /dev/null +++ b/charts/tractusx-connector-memory/values.yaml @@ -0,0 +1,313 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +# Default values for eclipse-dataspace-connector. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +fullnameOverride: "" +nameOverride: "" + +# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) +imagePullSecrets: [] + +customLabels: {} + +runtime: + image: + repository: "" + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "" + initContainers: [] + debug: + enabled: false + port: 1044 + suspendOnStart: false + internationalDataSpaces: + id: TXDC + description: Tractus-X Eclipse IDS Data Space Connector + title: "" + maintainer: "" + curator: "" + catalogId: TXDC-Catalog + livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first liveness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + readinessProbe: + # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first readiness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a readiness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # -- endpoints of the control plane + endpoints: + # -- default api for health checks, should not be added to any ingress + default: + # -- port for incoming api calls + port: 8080 + # -- path for incoming api calls + path: /api + # -- data management api, used by internal users, can be added to an ingress and must not be internet facing + data: + # -- port for incoming api calls + port: 8081 + # -- path for incoming api calls + path: /data + # -- authentication key, must be attached to each 'X-Api-Key' request header + authKey: "" + # -- validation api, only used by the data plane and should not be added to any ingress + validation: + # -- port for incoming api calls + port: 8082 + # -- path for incoming api calls + path: /validation + # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not + control: + # -- port for incoming api calls + port: 8083 + # -- path for incoming api calls + path: /control + # -- ids api, used for inter connector communication and must be internet facing + ids: + # -- port for incoming api calls + port: 8084 + # -- path for incoming api calls + path: /api/v1/ids + # -- observability api with unsecured access, must not be internet facing + observability: + # -- port for incoming API calls + port: 8085 + # -- observability api, provides /health /readiness and /liveness endpoints + path: /observability + # -- allow or disallow insecure access, i.e. access without authentication + insecure: true + public: + port: 8086 + path: /api/public + service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + annotations: {} + # -- additional labels for the pod + podLabels: {} + # -- additional annotations for the pod + podAnnotations: {} + # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment + podSecurityContext: + seccompProfile: + # -- Restrict a Container's Syscalls with seccomp + type: RuntimeDefault + # -- Runs all processes within a pod with a special uid + runAsUser: 10001 + # -- Processes within a pod will belong to this guid + runAsGroup: 10001 + # -- The owner for volumes and any files created within volumes will belong to this guid + fsGroup: 10001 + # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod + securityContext: + capabilities: + # -- Specifies which capabilities to drop to reduce syscall attack surface + drop: + - ALL + # -- Specifies which capabilities to add to issue specialized syscalls + add: [] + # -- Whether the root filesystem is mounted in read-only mode + readOnlyRootFilesystem: true + # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID + allowPrivilegeEscalation: false + # -- Requires the container to run without root privileges + runAsNonRoot: true + # -- The container's process will run with the specified uid + runAsUser: 10001 + # Extra environment variables that will be pass onto deployment pods + env: {} + # ENV_NAME: value + + # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # secretKeyRef: + # name: secret-name + # key: value_key + + # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from + envSecretNames: [] + # - first-secret + # - second-secret + + # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from + envConfigMapNames: [] + # - first-config-map + # - second-config-map + + ## Ingress declaration to expose the network service. + ingresses: + ## Public / Internet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.local" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - ids + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + ## Private / Intranet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.intranet" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - data + - control + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container + volumeMounts: [] + # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories + volumes: [] + # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container + resources: + {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + replicaCount: 1 + autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) + logging: |- + .level=INFO + org.eclipse.edc.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n + + # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes + nodeSelector: {} + # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes + tolerations: [] + # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on + affinity: {} + + url: + # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) + ids: "" + public: "" + readiness: "" + +vault: + # secrets can be seeded by supplying them in a comma separated list key1:secret2,key2:secret2 + secrets: "" + secretNames: + transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key + transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key + transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key + dapsPrivateKey: daps-private-key + dapsPublicKey: daps-public-key + +daps: + url: "" + clientId: "" + paths: + jwks: /jwks.json + token: /token + +backendService: + httpProxyTokenReceiverUrl: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + imagePullSecrets: [] diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 1161db178..6eded494c 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -68,7 +68,7 @@ spec: {{- else if .Values.vault.hashicorp.enabled }} image: "tractusx/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.azure.enabled }} - image: "tractusx/edc-controlplane-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-runtime-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else }} {{- fail "cannot choose control-plane image automatically based on configuration" }} {{- end }} diff --git a/docs/README.md b/docs/README.md index 259c2560b..17ad45b14 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,10 @@ # Tractus-X EDC -The Tractus-X EDC repository creates runnable applications out of EDC extensions from the [Eclipse DataSpace Connector](https://github.com/eclipse-edc/Connector) repository. +The Tractus-X EDC repository creates runnable applications out of EDC extensions from +the [Eclipse DataSpace Connector](https://github.com/eclipse-edc/Connector) repository. -When running a EDC connector from the Tractus-X EDC repository there are three setups to choose from. They only vary by using different extensions for +When running a EDC connector from the Tractus-X EDC repository there are three setups to choose from. They only vary by +using different extensions for - Resolving of Connector-Identities - Persistence of the Control-Plane-State @@ -12,11 +14,11 @@ When running a EDC connector from the Tractus-X EDC repository there are three s The three supported setups are. -- Setup 1: In Memory & Azure Vault - - [Control Plane](../edc-controlplane/edc-controlplane-memory/README.md) +- Setup 1: Pure in Memory **Not intended for production use!** + - [Control Plane](../edc-controlplane/edc-runtime-memory/README.md) - [IDS DAPS Extensions](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/iam/oauth2/daps) - In Memory Persistence done by using no extension - - [Azure Key Vault Extension](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/vault/azure-vault) + - In Memory Keyvault with seedable secrets. - [Data Plane](../edc-dataplane/edc-dataplane-azure-vault/README.md) - [Azure Key Vault Extension](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/vault/azure-vault) - Setup 2: PostgreSQL & Azure Vault diff --git a/docs/migration/Version_0.3.1_0.3.2.md b/docs/migration/Version_0.3.1_0.3.2.md index d6f49da29..4099e8c4b 100644 --- a/docs/migration/Version_0.3.1_0.3.2.md +++ b/docs/migration/Version_0.3.1_0.3.2.md @@ -2,7 +2,7 @@ ## Configuration of Azure KeyVault -When using Helm Charts that use the Azure KeyVault (`edc-controlplane-memory`, `edc-controlplane-postgres`) +When using Helm Charts that use the Azure KeyVault (`edc-runtime-memory`, `edc-controlplane-postgres`) it is now possible to select _either_ authentication via Client Secret (`azure.vault.secret`) or via certificate (`azure.vault.certificate`). diff --git a/edc-controlplane/build.gradle.kts b/edc-controlplane/build.gradle.kts index d27dbdf36..d7306dd1e 100644 --- a/edc-controlplane/build.gradle.kts +++ b/edc-controlplane/build.gradle.kts @@ -1,11 +1,10 @@ - plugins { `java-library` } dependencies { implementation(project(":edc-controlplane:edc-controlplane-base")) - implementation(project(":edc-controlplane:edc-controlplane-memory")) + implementation(project(":edc-controlplane:edc-runtime-memory")) implementation(project(":edc-controlplane:edc-controlplane-memory-hashicorp-vault")) implementation(project(":edc-controlplane:edc-controlplane-postgresql")) implementation(project(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault")) diff --git a/edc-controlplane/edc-controlplane-memory/README.md b/edc-controlplane/edc-runtime-memory/README.md similarity index 54% rename from edc-controlplane/edc-controlplane-memory/README.md rename to edc-controlplane/edc-runtime-memory/README.md index ca1f0bef7..caf7fe24e 100644 --- a/edc-controlplane/edc-controlplane-memory/README.md +++ b/edc-controlplane/edc-runtime-memory/README.md @@ -1,54 +1,64 @@ # EDC Control-Plane backed by In-Memory Stores +## Security + +### In-memory Vault implementation + +The goal of this extension is to provide an ephemeral, memory-based vault implementation that can be used in testing or +demo scenarios. + +Please not that this vault does not encrypt the secrets, they are held in memory in plain text at runtime! In addition, +its ephemeral nature makes it unsuitable for replicated/multi-instance scenarios, i.e. Kubernetes. + +> It is not a secure secret store, please do NOT use it in production workloads! + ## Building ```shell -./gradlew :edc-controlplane:edc-controlplane-memory:dockerize +./gradlew :edc-controlplane:edc-runtime-memory:dockerize ``` ## Configuration (configuration.properties) -Listed below are configuration keys needed to get the `edc-controlplane-memory` up and running. -Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). - -| Key | Required | Example | Description | -|--------------------------------------------------|----------|--------------------------------------|----------------------------| -| edc.api.auth.key | | password | default value: random UUID | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.data.port | X | 8181 | | -| web.http.data.path | X | /data | | -| web.http.validation.port | X | 8182 | | -| web.http.validation.path | X | /validation | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| web.http.ids.port | X | 8282 | | -| web.http.ids.path | X | /api/v1/ids | | -| edc.receiver.http.endpoint | X | | | -| edc.ids.title | | Eclipse Dataspace Connector | | -| edc.ids.description | | Eclipse Dataspace Connector | | -| edc.ids.id | | urn:connector:edc | | -| edc.ids.security.profile | | base | | -| edc.ids.endpoint | | | | -| edc.ids.maintainer | | | | -| edc.ids.curator | | | | -| edc.ids.catalog.id | | urn:catalog:default | | -| ids.webhook.address | | | | -| edc.hostname | | localhost | | -| edc.oauth.token.url | X | | | -| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | -| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | -| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.name | X | my-vault-name | | -| edc.vault.clientsecret | X | 34-chars-secret | | -| edc.transfer.proxy.endpoint | X | | | -| edc.transfer.proxy.token.signer.privatekey.alias | X | | | +Listed below are configuration keys needed to get the `edc-runtime-memory` up and running. +Details regarding each configuration property can be found at +the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). + +| Key | Required | Example | Description | +|--------------------------------------------------|----------|-------------------------------------|----------------------------| +| edc.api.auth.key | | password | default value: random UUID | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | /data | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | | | +| edc.ids.maintainer | | | | +| edc.ids.curator | | | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | | | +| edc.hostname | | localhost | | +| edc.oauth.token.url | X | | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.transfer.proxy.endpoint | X | | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | | | ### Example configuration.properties -JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. +JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` +within the container. ```shell # Create configuration.properties @@ -88,12 +98,6 @@ edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault edc.oauth.private.key.alias=key-to-private-key-in-keyvault edc.oauth.client.id=daps-oauth-client-id -# Azure vault related configuration -edc.vault.clientid=00000000-1111-2222-3333-444444444444 -edc.vault.tenantid=55555555-6666-7777-8888-999999999999 -edc.vault.name=my-vault-name -edc.vault.clientsecret=34-chars-secret - # Control- / Data- Plane configuration edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public edc.transfer.proxy.token.signer.privatekey.alias=azure-vault-token-signer-private-key @@ -115,24 +119,13 @@ java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [ EOF ``` -### Example opentelemetry.properties - -```shell -# Create opentelemetry.properties -export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) -cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} -otel.javaagent.enabled=true -otel.javaagent.debug=false -EOF -``` - ## Running ```shell docker run \ + -e SECRETS="key1:secret1,key2:secret2" \ -p 8080:8080 -p 8181:8181 -p 8182:8182 -p 8282:8282 -p 9090:9090 -p 9999:9999 \ -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ - -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ - -i edc-controlplane-memory:latest + -i edc-runtime-memory:latest ``` diff --git a/edc-controlplane/edc-controlplane-memory/build.gradle.kts b/edc-controlplane/edc-runtime-memory/build.gradle.kts similarity index 77% rename from edc-controlplane/edc-controlplane-memory/build.gradle.kts rename to edc-controlplane/edc-runtime-memory/build.gradle.kts index 1254a3634..304584058 100644 --- a/edc-controlplane/edc-controlplane-memory/build.gradle.kts +++ b/edc-controlplane/edc-runtime-memory/build.gradle.kts @@ -1,5 +1,3 @@ -import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage - plugins { `java-library` id("application") @@ -7,11 +5,12 @@ plugins { } dependencies { - runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) + implementation(edc.spi.core) + runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) { + exclude(module = "data-encryption") + } + runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) runtimeOnly(edc.core.controlplane) - runtimeOnly(edc.azure.vault) - runtimeOnly(edc.azure.identity) - } tasks.withType { diff --git a/edc-controlplane/edc-controlplane-memory/notice.md b/edc-controlplane/edc-runtime-memory/notice.md similarity index 90% rename from edc-controlplane/edc-controlplane-memory/notice.md rename to edc-controlplane/edc-runtime-memory/notice.md index c58f81b30..f33bb6885 100644 --- a/edc-controlplane/edc-controlplane-memory/notice.md +++ b/edc-controlplane/edc-runtime-memory/notice.md @@ -2,7 +2,7 @@ An EDC Control Plane using memory-based storage, and Azure KeyVault as secret store. -DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory +DockerHub: https://hub.docker.com/r/tractusx/edc-runtime-memory Eclipse Tractus-X product(s) installed within the image: @@ -10,7 +10,7 @@ Eclipse Tractus-X product(s) installed within the image: - GitHub: https://github.com/eclipse-tractusx/tractusx-edc - Project home: https://projects.eclipse.org/projects/automotive.tractusx -- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile - Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) ## Used base image diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile similarity index 61% rename from edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile rename to edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile index d248e8131..de17857c4 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile @@ -1,4 +1,5 @@ # +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # Copyright (c) 2023 ZF Friedrichshafen AG # Copyright (c) 2022,2023 Mercedes-Benz Tech Innovation GmbH # Copyright (c) 2021,2023 Contributors to the Eclipse Foundation @@ -18,13 +19,6 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.3 as otel - -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -HEALTHCHECK NONE - -RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR @@ -46,18 +40,10 @@ RUN adduser \ USER "$APP_USER" WORKDIR /app -COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-controlplane.jar HEALTHCHECK NONE -CMD ["java", \ - "-javaagent:/app/opentelemetry-javaagent.jar", \ - "-Dedc.fs.config=/app/configuration.properties", \ - "-Djava.util.logging.config.file=/app/logging.properties", \ - "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ - "-Dotel.metrics.exporter=prometheus", \ - "-Dotel.exporter.prometheus.port=9090", \ - "-Djava.security.egd=file:/dev/urandom", \ - "-jar", \ - "edc-controlplane.jar"] +# need the sh -c syntax so that the SECRETS variable gets expanded +# use the "exec" syntax so that SIGINT reaches the JVM -> graceful termination +CMD ["sh", "-c", "exec java -Dedc.fs.config=/app/configuration.properties -Dedc.vault.secrets=\"${SECRETS}\" -Djava.util.logging.config.file=/app/logging.properties -Djava.security.egd=file:/dev/urandom -jar edc-controlplane.jar"] diff --git a/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVault.java b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVault.java new file mode 100644 index 000000000..9b92a83c0 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVault.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.vault.memory; + +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class InMemoryVault implements Vault { + private final Map secrets = new ConcurrentHashMap<>(); + private final Monitor monitor; + + public InMemoryVault(Monitor monitor) { + this.monitor = monitor; + } + + @Override + public @Nullable String resolveSecret(String s) { + monitor.debug("resolving secret " + s); + return secrets.getOrDefault(s, null); + } + + @Override + public Result storeSecret(String s, String s1) { + monitor.debug("storing secret " + s); + secrets.put(s, s1); + return Result.success(); + } + + @Override + public Result deleteSecret(String s) { + monitor.debug("deleting secret " + s); + return secrets.remove(s) == null ? + Result.failure("Secret with key " + s + " does not exist") : + Result.success(); + } +} diff --git a/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java new file mode 100644 index 000000000..c8d2cc7d2 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ +package org.eclipse.tractusx.edc.vault.memory; + +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.runtime.metamodel.annotation.Provides; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.security.*; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; + +import java.util.stream.Stream; + +@Provides({PrivateKeyResolver.class, CertificateResolver.class}) +@Extension(value = "In-memory vault extension", categories = {"vault", "security"}) +public class VaultMemoryExtension implements ServiceExtension { + + @Setting(value = "Secrets with which the vault gets initially populated. Specify as comma-separated list of key:secret pairs.") + public static final String VAULT_MEMORY_SECRETS_PROPERTY = "edc.vault.secrets"; + public static final String NAME = "In-Memory Vault Extension"; + + @Override + public String name() { + return NAME; + } + + @Provider + public Vault createInMemVault(ServiceExtensionContext context) { + var seedSecrets = context.getSetting(VAULT_MEMORY_SECRETS_PROPERTY, null); + var vault = new InMemoryVault(context.getMonitor()); + context.registerService(PrivateKeyResolver.class, new VaultPrivateKeyResolver(vault)); + context.registerService(CertificateResolver.class, new VaultCertificateResolver(vault)); + if (seedSecrets != null) { + Stream.of(seedSecrets.split(";")) + .filter(pair -> pair.contains(":")) + .map(kvp -> kvp.split(":", 2)) + .filter(kvp -> kvp.length >= 2) + .forEach(pair -> vault.storeSecret(pair[0], pair[1])); + } + return vault; + } +} diff --git a/edc-controlplane/edc-runtime-memory/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-controlplane/edc-runtime-memory/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..b105388ea --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,21 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +org.eclipse.tractusx.edc.vault.memory.VaultMemoryExtension diff --git a/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVaultTest.java b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVaultTest.java new file mode 100644 index 000000000..c00ae8180 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVaultTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.vault.memory; + +import org.eclipse.edc.spi.monitor.Monitor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class InMemoryVaultTest { + + private InMemoryVault vault; + + @BeforeEach + void setUp() { + vault = new InMemoryVault(mock(Monitor.class)); + } + + @Test + void resolveSecret() { + assertThat(vault.resolveSecret("key")).isNull(); + vault.storeSecret("key", "secret"); + assertThat(vault.resolveSecret("key")).isEqualTo("secret"); + } + + @Test + void storeSecret() { + assertThat(vault.storeSecret("key", "value1").succeeded()).isTrue(); + assertThat(vault.resolveSecret("key")).isEqualTo("value1"); + assertThat(vault.storeSecret("key", "value2").succeeded()).isTrue(); + assertThat(vault.resolveSecret("key")).isEqualTo("value2"); + } + + @Test + void deleteSecret() { + assertThat(vault.deleteSecret("key").succeeded()).isFalse(); + assertThat(vault.storeSecret("key", "value1").succeeded()).isTrue(); + assertThat(vault.deleteSecret("key").succeeded()).isTrue(); + assertThat(vault.resolveSecret("key")).isNull(); + + } +} diff --git a/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java new file mode 100644 index 000000000..bec589d79 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.vault.memory; + +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +class VaultMemoryExtensionTest { + private VaultMemoryExtension extension; + private ServiceExtensionContext context; + private Monitor monitor; + + @BeforeEach + void setup() { + extension = new VaultMemoryExtension(); + context = mock(ServiceExtensionContext.class); + monitor = mock(Monitor.class); + when(context.getMonitor()).thenReturn(monitor); + } + + @Test + void name() { + assertThat(extension.name()).isEqualTo("In-Memory Vault Extension"); + } + + @ParameterizedTest + @ValueSource(strings = {"key1:", "key1:value1", "key1:value1;", ";key1:value1", ";sdf;key1:value1"}) + void createInMemVault_validString(String secret) { + when(context.getSetting(eq(VaultMemoryExtension.VAULT_MEMORY_SECRETS_PROPERTY), eq(null))).thenReturn(secret); + extension.createInMemVault(context); + verify(monitor, times(1)).debug(anyString()); + } +} diff --git a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml index 57779134b..07c1e0b3b 100644 --- a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml +++ b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml @@ -8,19 +8,6 @@ install: postgresql: true vault: true minio: true - backendservice: true - -################### -# Backend Service # -################### -backend: - fullnameOverride: "backend" - service: - type: NodePort - frontend: - port: 8080 - backend: - port: 8081 ######## diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java index ddc715c9b..0d380685e 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java @@ -22,7 +22,7 @@ public Negotiation(String id) { public void waitUntilComplete(DataManagementAPI dataManagementAPI) { await() - .pollDelay(Duration.ofMillis(2000)) + .pollDelay(Duration.ofMillis(5000)) .atMost(Timeouts.CONTRACT_NEGOTIATION) .until(() -> isComplete(dataManagementAPI)); } diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore b/edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml new file mode 100644 index 000000000..613b1f45c --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: v2 +name: ids-daps +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/README.md b/edc-tests/deployment/src/main/resources/helm/omejdn/README.md new file mode 100644 index 000000000..f85a94889 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/README.md @@ -0,0 +1,21 @@ +# Omejdn DAPS + +This chart deployes an [IDS Omejdn DAPS](https://github.com/Fraunhofer-AISEC/omejdn-server). + +Two Eclipse Dataspace Connectors need to be registered at the same DAPS instance, to be able to talk to each other. Each connector is registered in the DAPS by an unique client ID and a correpsonding client certificate. + +New connectors are configured in the omejdn _values.yaml_. + +In each Eclipse Dataspace Connector configure the following properties to use the DAPS. + +```properties + edc.oauth.client.id= + + edc.oauth.provider.jwks.url="http://:4567/.well-known/jwks.json" + edc.oauth.token.url="http://:4567/token" + + edc.oauth.private.key.alias= + edc.oauth.public.key.alias= + + edc.oauth.provider.audience=idsc:IDS_CONNECTORS_ALL +``` diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl new file mode 100644 index 000000000..95b115eee --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "omejdn.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "omejdn.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "omejdn.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "omejdn.labels" -}} +helm.sh/chart: {{ include "omejdn.chart" . }} +{{ include "omejdn.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "omejdn.selectorLabels" -}} +app.kubernetes.io/name: {{ include "omejdn.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "omejdn.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "omejdn.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml new file mode 100644 index 000000000..3d3e17c1f --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml @@ -0,0 +1,73 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +data: + scope_mapping.yml: |- + --- + idsc:IDS_CONNECTOR_ATTRIBUTES_ALL: + - referringConnector + + omejdn.yml: |- + --- + host: http://ids-daps:4567/ + path_prefix: '' + bind_to: 0.0.0.0 + allow_origin: "*" + app_env: debug + openid: false + user_backend: + - yaml + user_backend_default: yaml + accept_audience: idsc:IDS_CONNECTORS_ALL + issuer: http://ids-daps:4567/ + environment: development + default_audience: + - idsc:IDS_CONNECTORS_ALL + access_token: + expiration: 3600 + algorithm: RS256 + id_token: + expiration: 3600 + algorithm: RS256 + + plugins.yml: |- + --- + plugins: + token_user_attributes: + + clients.yml: |- + --- + - client_id: data-plane-oauth2 + client_secret: supersecret + name: provision oauth2 + grant_types: + - client_credentials + token_endpoint_auth_method: client_secret_post + scope: openid +{{- range $i, $val := .Values.connectors }} + - client_id: {{ quote $val.id }} + name: {{ quote $val.name }} + token_endpoint_auth_method: private_key_jwt + grant_types: + - client_credentials + scope: + - idsc:IDS_CONNECTOR_ATTRIBUTES_ALL + attributes: + - key: idsc + value: IDS_CONNECTOR_ATTRIBUTES_ALL + - key: securityProfile + value: idsc:BASE_SECURITY_PROFILE + {{- range $key, $value := $val.attributes }} + - key: {{ $key }} + value: {{ $value }} + {{- end }} + redirect_uri: http://localhost:4200 +{{ end -}} + + +{{- range $i, $val := .Values.connectors }} + {{ $val.name }}: {{ quote $val.certificate | toString }} +{{ end -}} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml new file mode 100644 index 000000000..289476122 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml @@ -0,0 +1,149 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "omejdn.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "omejdn.selectorLabels" . | nindent 8 }} + spec: + {{- if .Values.imagePullSecret.dockerconfigjson }} + imagePullSecrets: + - name: {{ include "omejdn.fullname" . }}-imagepullsecret + {{- else }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} + serviceAccountName: {{ include "omejdn.serviceAccountName" . }} + automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + initContainers: + - name: init-daps-pvc + image: alpine + command: + - "sh" + - "-c" + args: + - | + cp /opt/config/omejdn.yml /etc/daps/omejdn.yml + cp /opt/config/clients.yml /etc/daps/clients.yml + cp /opt/config/plugins.yml /etc/daps/plugins.yml + cp /opt/config/scope_mapping.yml /etc/daps/scope_mapping.yml + apk add --update openssl + openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /etc/keys/omejdn/omejdn.key \ + -subj "/C=DE/ST=Berlin/L=Berlin/O=TractusX-EDC-Test, Inc./OU=DE" + volumeMounts: + - mountPath: /etc/daps + name: config-dir + - mountPath: /etc/keys/omejdn + name: omejdn-key-dir + - mountPath: /opt/config/omejdn.yml + name: omejdn-config + subPath: omejdn.yml + - mountPath: /opt/config/scope_mapping.yml + name: scope-mapping + subPath: scope_mapping.yml + - mountPath: /opt/config/clients.yml + name: clients-config + subPath: clients.yml + - mountPath: /opt/config/plugins.yml + name: plugins-config + subPath: plugins.yml + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - mountPath: /opt/config/ + name: config-dir + - mountPath: /opt/keys/omejdn/omejdn.key + name: omejdn-key-dir + subPath: omejdn.key + - mountPath: /opt/keys/clients/ + name: client-certificates + ports: + - name: http + containerPort: 4567 + protocol: TCP + livenessProbe: + httpGet: + path: /jwks.json + port: http + readinessProbe: + httpGet: + path: /jwks.json + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: OMEJDN_JWT_AUD_OVERRIDE + value: "idsc:IDS_CONNECTORS_ALL" + - name: OMEJDN_PLUGINS + value: "config/plugins.yml" + volumes: + - name: config-dir + emptyDir: {} + - name: omejdn-key-dir + emptyDir: {} + - name: omejdn-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: omejdn.yml + path: omejdn.yml + - name: scope-mapping + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: scope_mapping.yml + path: scope_mapping.yml + - name: clients-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: clients.yml + path: clients.yml + - name: plugins-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: plugins.yml + path: plugins.yml + - name: client-certificates + configMap: + name: {{ include "omejdn.fullname" . }} + items: + {{- range $i, $val := .Values.connectors }} + - key: {{ $val.name }} + path: {{ $val.id }}.cert + {{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml new file mode 100644 index 000000000..ce2a70957 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "omejdn.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml new file mode 100644 index 000000000..d7c1d31d7 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml @@ -0,0 +1,13 @@ +{{- if .Values.imagePullSecret.dockerconfigjson }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "edc-dataplane.labels" . | nindent 4 }} +data: + .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} +type: kubernetes.io/dockerconfigjson +{{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml new file mode 100644 index 000000000..57dfe3921 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "omejdn.selectorLabels" . | nindent 4 }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml new file mode 100644 index 000000000..17baf8239 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "omejdn.serviceAccountName" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml new file mode 100644 index 000000000..ae82cd1d6 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml @@ -0,0 +1,91 @@ +--- +# Default values for omejdn. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- Specifies how many replicas of a deployed pod shall be created during the deployment +# Note: If horizontal pod autoscaling is enabled this setting has no effect +replicaCount: 1 + +image: + # -- Which omjedn container image to use + repository: ghcr.io/fraunhofer-aisec/omejdn-server + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "1.7.1" + +imagePullSecret: + # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). + # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. + dockerconfigjson: "" + +# -- Overrides the charts name +nameOverride: "" + +# -- Overrides the releases full name +fullnameOverride: "" + +serviceAccount: + # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release + create: true + # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account + annotations: {} + # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template + name: "" + +# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod +automountServiceAccountToken: false + +# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) +podAnnotations: {} + +# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment +podSecurityContext: {} + +# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod +securityContext: {} + +service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. + port: 4567 + +# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod +resources: {} + +autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + +# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. +nodeSelector: {} + +# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. +tolerations: [] + +# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. +affinity: {} + +# List of connector clients. Certificate and Client-ID must be configured in parallel. +#
+# Example Connector: +# - id: grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 +# name: my-connector +# attributes: +# issuerConnector: http://localhost:8080/ +# certificate: |- +# -----BEGIN CERTIFICATE----- +# foo +# -----END CERTIFICATE----- +connectors: [] diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore new file mode 100644 index 000000000..8681aba50 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore @@ -0,0 +1,4 @@ +# ignore downloaded helm depdencies +charts/ + +Chart.lock diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore new file mode 100644 index 000000000..8c60d7821 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore @@ -0,0 +1,24 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +docs diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml new file mode 100644 index 000000000..6e7f24fe5 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml @@ -0,0 +1,54 @@ +--- +apiVersion: v2 +name: all-in-one +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" + +dependencies: + # IDS Dynamic Attribute Provisioning Service (IAM) + - name: ids-daps + version: 0.0.1 + repository: "file://../omejdn" + alias: idsdaps + condition: install.daps + + # HashiCorp Vault + - name: vault + alias: vault + version: 0.20.0 + repository: https://helm.releases.hashicorp.com + condition: install.vault + + # PostgreSQL + - name: postgresql + alias: postgresql + version: 12.1.6 + repository: https://charts.bitnami.com/bitnami + condition: install.postgresql + + # MinIo + - name: minio + alias: minio + repository: https://charts.min.io + version: 4.1.0 + condition: install.minio diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md new file mode 100644 index 000000000..e927d7bc5 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md @@ -0,0 +1,54 @@ +# Supporting Infrastructure Deployment + +The Supporting Infrastructure Deployment creates a complete, independent and already configured EDC test environment. +During the automated business tests, these infrastructure components are deployed together with two connectors (Plato & Sokrates). + +This deployment could also be used as + +- reference setup for teams, that want to create their own connector +- standalone infrastructure to try things out + +This deployment should **never** be used + +- in **any** production or near production environments +- in **any** long living internet facing connector setups + +## Omejdn DAPS + +The Dynamic Attribute Provisioning Service (DAPS) is a component of the IDS Ecosystem. +The Fraunhofer Institute has created a DAPS reference implementation, the Omejdn +DAPS ([link](https://github.com/Fraunhofer-AISEC/omejdn-server)). This deplyoment configures and deployes a instance of +this reference implementation. + +Definition of DAPS from the IDS Reference architecture v3.0: + +> The Identity Provider acts as an agent for the International +> Data Spaces Association. It is responsible for issuing technical identities to parties that have been approved to become +> Participants in the International Data Spaces. The Identity +> Provider is instructed to issue identities based on approved +> roles (e.g., App Store or App Provider). Only if equipped with +> such an identity, an entity is allowed to participate in the International Data Spaces + +Also, please note, that the Omejdn DAPS is meant as research sandbox and should not be used in anq +productive environment. + +> **IMPORTANT:** Omejdn is meant to be a research sandbox in which we can (re)implement standard protocols and +> potentially extend and modify functionality under the hood to support research projects. Use at your own +> risk! ([source](https://github.com/Fraunhofer-AISEC/omejdn-server)) + +## HashiCorp Vault + +The Control- and Data Plane persist confidential in the vault and persist and communicate using only the secret +names. Hence, it is not possible to run a connector without an instance of a vault. + +## PostgreSQL + +This database is used to persist the state of the Control Plane. + +## Setup + +Simply execute the following comment in a shell: + +```shell +helm install infra edc-tests/deployment/src/main/resources/helm/test-infrastructure --update-dependencies +``` diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml new file mode 100644 index 000000000..feba29cc4 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml @@ -0,0 +1,185 @@ +--- + +########### +# Install # +########### +install: + daps: true + postgresql: true + vault: true + minio: false + + +######## +# DAPS # +######## +idsdaps: + fullnameOverride: "ids-daps" + connectors: + - id: E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65 + name: sokrates + attributes: + referringConnector: http://sokrates-controlplane/BPNSOKRATES + # Must be the same certificate that is stores in section 'sokrates-vault' + certificate: |- + -----BEGIN CERTIFICATE----- + MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ + TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK + DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD + DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw + 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa + llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV + grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 + PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 + ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT + MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH + LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd + iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E + 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 + S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r + uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB + UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + -----END CERTIFICATE----- + +############## +# PostgreSQL # +############## +postgresql: + fullnameOverride: "postgresql" + primary: + persistence: + enabled: false + readReplicas: + persistence: + enabled: false + auth: + database: "edc" + username: "user" + password: "password" + +######### +# MINIO # +######### +minio: + fullnameOverride: minio + replicas: 2 + drivesPerNode: 0 + serviceAccount: + create: false + persistence: + size: 128Mi + resources: + requests: + memory: 128Mi + service: + type: NodePort + control: + port: 9000 + users: + - accessKey: qwerty123 + secretKey: qwerty123 + policy: customBucketPolicy + buckets: + # in some cases the minio API acts strange if there exists no bucket at all + - name: dummybucket + policy: none + purge: true + policies: + - name: customBucketPolicy + statements: + - resources: + - 'arn:aws:s3:::*' + actions: + - "s3:PutObject" + - "s3:ListBucket" + - "s3:CreateBucket" + - "s3:GetObject" + - "s3:DeleteObject" + - "s3:DeleteBucket" + +######### +# VAULT # +######### +vault: + fullnameOverride: "vault" + injector: + enabled: false + server: + dev: + enabled: true + devRootToken: "root" + # Must be the same certificate that is configured in section 'ids-daps' + postStart: + - "sh" + - "-c" + - | + { + + sleep 5 + + /bin/vault kv put secret/sokrates/data-encryption-aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== + + cat << EOF | /bin/vault kv put secret/sokrates/daps/daps-key content=- + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM + wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ + FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 + 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ + ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE + sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc + RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z + d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm + hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm + cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh + FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F + MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e + uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb + ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 + z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 + h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ + vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB + 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 + hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 + dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 + Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 + IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT + 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr + 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 + u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B + AjWFbUiBCFOo+gpAFcQGrkOQHA== + -----END PRIVATE KEY----- + EOF + + cat << EOF | /bin/vault kv put secret/sokrates/daps/daps-crt content=- + -----BEGIN CERTIFICATE----- + MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ + TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK + DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD + DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw + 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa + llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV + grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 + PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 + ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT + MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH + LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd + iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E + 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 + S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r + uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB + UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + -----END CERTIFICATE----- + EOF + } diff --git a/settings.gradle.kts b/settings.gradle.kts index 22ac0d73a..e22d4aacc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,7 @@ include(":edc-tests:cucumber") // modules for controlplane artifacts include(":edc-controlplane") include(":edc-controlplane:edc-controlplane-base") -include(":edc-controlplane:edc-controlplane-memory") +include(":edc-controlplane:edc-runtime-memory") include(":edc-controlplane:edc-controlplane-memory-hashicorp-vault") include(":edc-controlplane:edc-controlplane-postgresql") include(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault") @@ -137,38 +137,34 @@ dependencyResolutionManagement { library("micrometer-jersey", "org.eclipse.edc", "jersey-micrometer").versionRef("edc") library("micrometer-jetty", "org.eclipse.edc", "jetty-micrometer").versionRef("edc") library("monitor-jdklogger", "org.eclipse.edc", "monitor-jdk-logger").versionRef("edc") - library( - "transfer.dynamicreceiver", - "org.eclipse.edc", - "transfer-pull-http-dynamic-receiver" - ).versionRef("edc") + library("transfer.dynamicreceiver", "org.eclipse.edc", "transfer-pull-http-dynamic-receiver").versionRef("edc") library("transfer.receiver", "org.eclipse.edc", "transfer-pull-http-receiver").versionRef("edc") bundle( - "connector", - listOf("boot", "core-connector", "core-jersey", "core-controlplane", "api-observability") + "connector", + listOf("boot", "core-connector", "core-jersey", "core-controlplane", "api-observability") ) bundle( - "dpf", - listOf("dpf-transfer", "dpf-selector-core", "dpf-selector-client", "spi-dataplane-selector") + "dpf", + listOf("dpf-transfer", "dpf-selector-core", "dpf-selector-client", "spi-dataplane-selector") ) bundle( - "sqlstores", - listOf( - "sql-assetindex", - "sql-contract-definition", - "sql-contract-negotiation", - "sql-transferprocess", - "sql-policydef" - ) + "sqlstores", + listOf( + "sql-assetindex", + "sql-contract-definition", + "sql-contract-negotiation", + "sql-transferprocess", + "sql-policydef" + ) ) bundle( - "monitoring", - listOf("micrometer-core", "micrometer-jersey", "micrometer-jetty") + "monitoring", + listOf("micrometer-core", "micrometer-jersey", "micrometer-jetty") // listOf("micrometer-core", "micrometer-jersey", "micrometer-jetty", "monitor-jdklogger") ) } From 689f778824da3a671c1ad13be817bdcd8c8e58b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Apr 2023 07:54:30 +0200 Subject: [PATCH 080/132] chore(deps): bump org.slf4j:slf4j-api from 2.0.3 to 2.0.7 (#234) Bumps [org.slf4j:slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.3 to 2.0.7. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.3...v_2.0.7) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- edc-tests/cucumber/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 13dc385b0..809528492 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,7 +52,7 @@ allprojects { } dependencies { implementation("org.projectlombok:lombok:1.18.26") - implementation("org.slf4j:slf4j-api:2.0.5") + implementation("org.slf4j:slf4j-api:2.0.7") // this is used to counter version conflicts between the JUnit version pulled in by the plugin, // and the one expected by IntelliJ testImplementation(platform("org.junit:junit-bom:5.9.2")) diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index 17f5233d7..f2e40439d 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { testImplementation("org.junit.platform:junit-platform-suite:1.9.2") testImplementation("io.cucumber:cucumber-java:7.11.2") testImplementation("io.cucumber:cucumber-junit-platform-engine:7.11.2") - testImplementation("org.slf4j:slf4j-api:2.0.3") + testImplementation("org.slf4j:slf4j-api:2.0.7") testImplementation(libs.restAssured) testImplementation(libs.postgres) testImplementation(libs.awaitility) From abb352a8048fe85c4751a38649091fb12f18bf4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Apr 2023 07:55:25 +0200 Subject: [PATCH 081/132] chore(deps): bump com.azure:azure-security-keyvault-secrets (#235) Bumps [com.azure:azure-security-keyvault-secrets](https://github.com/Azure/azure-sdk-for-java) from 4.5.4 to 4.6.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-security-keyvault-keys_4.5.4...azure-cosmos_4.6.0) --- updated-dependencies: - dependency-name: com.azure:azure-security-keyvault-secrets dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index bde51831c..e360497a1 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -9,7 +9,7 @@ dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) implementation(edc.azure.vault) implementation(edc.azure.identity) - implementation("com.azure:azure-security-keyvault-secrets:4.5.4") + implementation("com.azure:azure-security-keyvault-secrets:4.6.0") } tasks.withType { From 7c03aec007be01e1a226b864a5c3cac56a580fea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Apr 2023 07:55:45 +0200 Subject: [PATCH 082/132] chore(deps): bump com.diffplug.spotless from 6.15.0 to 6.18.0 (#236) Bumps com.diffplug.spotless from 6.15.0 to 6.18.0. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 809528492..1d4e2be61 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { `maven-publish` `jacoco-report-aggregation` id("io.freefair.lombok") version "6.6.2" - id("com.diffplug.spotless") version "6.15.0" + id("com.diffplug.spotless") version "6.18.0" id("com.github.johnrengelman.shadow") version "8.0.0" id("com.bmuschko.docker-remote-api") version "9.3.1" id("org.sonarqube") version "4.0.0.2929" From 192c4421367b4cd12b56a9523e0ddcefb4b125d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Apr 2023 06:08:43 +0000 Subject: [PATCH 083/132] chore(deps): bump com.github.johnrengelman.shadow from 8.0.0 to 8.1.1 (#237) --- build.gradle.kts | 2 +- .../edc-controlplane-memory-hashicorp-vault/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- edc-controlplane/edc-controlplane-postgresql/build.gradle.kts | 2 +- edc-controlplane/edc-runtime-memory/build.gradle.kts | 2 +- edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts | 2 +- edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts | 2 +- edc-tests/runtime/build.gradle.kts | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1d4e2be61..b494219b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { `jacoco-report-aggregation` id("io.freefair.lombok") version "6.6.2" id("com.diffplug.spotless") version "6.18.0" - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" id("com.bmuschko.docker-remote-api") version "9.3.1" id("org.sonarqube") version "4.0.0.2929" } diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts index eb7aef8fe..3fb52b522 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts @@ -3,7 +3,7 @@ import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts index 7c8a46f54..637b63765 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts @@ -4,7 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts index b69e3d010..5888c34c4 100644 --- a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts @@ -4,7 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-controlplane/edc-runtime-memory/build.gradle.kts b/edc-controlplane/edc-runtime-memory/build.gradle.kts index 304584058..372ec486d 100644 --- a/edc-controlplane/edc-runtime-memory/build.gradle.kts +++ b/edc-controlplane/edc-runtime-memory/build.gradle.kts @@ -1,7 +1,7 @@ plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index e360497a1..02d29b7db 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -2,7 +2,7 @@ plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts index 7f53f8c5c..0ae98dd2d 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts @@ -2,7 +2,7 @@ plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-tests/runtime/build.gradle.kts b/edc-tests/runtime/build.gradle.kts index 6f8a370af..0123162fb 100644 --- a/edc-tests/runtime/build.gradle.kts +++ b/edc-tests/runtime/build.gradle.kts @@ -15,7 +15,7 @@ plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } From c0aa1d4a24b28a4ee6c9e0156da35029ae10cd08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Apr 2023 06:21:18 +0000 Subject: [PATCH 084/132] chore(deps): bump io.freefair.lombok from 6.6.2 to 8.0.1 (#238) --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index b494219b2..1f545aea5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { `java-library` `maven-publish` `jacoco-report-aggregation` - id("io.freefair.lombok") version "6.6.2" + id("io.freefair.lombok") version "8.0.1" id("com.diffplug.spotless") version "6.18.0" id("com.github.johnrengelman.shadow") version "8.1.1" id("com.bmuschko.docker-remote-api") version "9.3.1" From 0b9c11cfe595793a3468afe36a3327beaf333c0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 05:08:43 +0000 Subject: [PATCH 085/132] chore(deps): bump org.flywaydb:flyway-core from 9.15.2 to 9.16.3 (#242) --- edc-extensions/postgresql-migration/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-extensions/postgresql-migration/build.gradle.kts b/edc-extensions/postgresql-migration/build.gradle.kts index 8d7b1fa05..cb04877c0 100644 --- a/edc-extensions/postgresql-migration/build.gradle.kts +++ b/edc-extensions/postgresql-migration/build.gradle.kts @@ -11,5 +11,5 @@ dependencies { implementation(edc.sql.assetindex) implementation(edc.sql.core) - implementation("org.flywaydb:flyway-core:9.15.2") + implementation("org.flywaydb:flyway-core:9.16.3") } From d268bf029d61c84b9643f0dd3a75514115f03fb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 07:08:57 +0200 Subject: [PATCH 086/132] chore(deps): bump com.google.code.gson:gson from 2.10 to 2.10.1 (#243) Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.10 to 2.10.1. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.10...gson-parent-2.10.1) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-tests/cucumber/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index f2e40439d..2628ce71e 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -16,7 +16,7 @@ dependencies { implementation(project(":edc-extensions:transferprocess-sftp-provisioner")) - testImplementation("com.google.code.gson:gson:2.10") + testImplementation("com.google.code.gson:gson:2.10.1") testImplementation("org.apache.httpcomponents:httpclient:4.5.14") testImplementation("org.junit.platform:junit-platform-suite:1.9.2") testImplementation("io.cucumber:cucumber-java:7.11.2") From f30e78a78120068f65791902693df0331c91bc0c Mon Sep 17 00:00:00 2001 From: Garrett Smith <42892027+gcs14@users.noreply.github.com> Date: Wed, 19 Apr 2023 00:22:28 -0500 Subject: [PATCH 087/132] refactor: update GitHub output command to current version (#233) * refactor GitHub output command to current version * Remove curly braces from output statement --- .github/workflows/build.yaml | 8 ++++---- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/helm-lint.yaml | 2 +- .github/workflows/publish-new-release.yml | 2 +- .github/workflows/trivy.yml | 2 +- .github/workflows/veracode.yaml | 4 ++-- .github/workflows/verify.yaml | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2c2dda9c2..a701ae362 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -60,10 +60,10 @@ jobs: - name: Check whether secrets exist id: secret-presence run: | - [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "::set-output name=SONAR_TOKEN::true" - [ ! -z "${{ secrets.GPG_PRIVATE_KEY }}" ] && echo "::set-output name=GPG_PRIVATE_KEY::true" - [ ! -z "${{ secrets.GPG_PASSPHRASE }}" ] && echo "::set-output name=GPG_PASSPHRASE::true" - [ ! -z "${{ secrets.DOCKER_HUB_TOKEN }}" ] && echo "::set-output name=DOCKER_HUB_TOKEN::true" + [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "SONAR_TOKEN=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.GPG_PRIVATE_KEY }}" ] && echo "GPG_PRIVATE_KEY=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.GPG_PASSPHRASE }}" ] && echo "GPG_PASSPHRASE=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.DOCKER_HUB_TOKEN }}" ] && echo "DOCKER_HUB_TOKEN=true" >> $GITHUB_OUTPUT exit 0 build-extensions: diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 248f61bc4..22222ba8a 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -68,7 +68,7 @@ jobs: git add CHANGELOG.md gradle.properties $(find charts -name Chart.yaml) $(find charts -name README.md) git commit --message "Prepare release ${{ github.event.inputs.version }}" - echo "::set-output name=commit::$(git rev-parse HEAD)" + echo "commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - name: Push new branch run: git push origin release/${{ github.event.inputs.version }} diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index ae94c84a7..7640d0a38 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -52,7 +52,7 @@ jobs: run: | changed=$(ct list-changed --config ct.yaml --target-branch main) if [[ -n "$changed" ]]; then - echo "::set-output name=changed::true" + echo "changed=true" >> $GITHUB_OUTPUT fi - name: chart-testing (lint) diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 373c892e7..06ee58b61 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -37,7 +37,7 @@ jobs: name: Output release version id: release-version run: | - echo "::set-output name=RELEASE_VERSION::${{ env.RELEASE_VERSION }}" + echo "RELEASE_VERSION=${{ env.RELEASE_VERSION }}" >> $GITHUB_OUTPUT # Release: Maven Artifacts maven-release: diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index c315e8a07..819fec089 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -27,7 +27,7 @@ jobs: - name: Resolve git 7-chars sha id: git-sha7 run: | - echo "::set-output name=SHA7::${GITHUB_SHA::7}" + echo "SHA7=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT trivy-analyze-config: runs-on: ubuntu-latest diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index bba9df1b5..c3e7cb7a1 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -16,8 +16,8 @@ jobs: - name: Check whether secrets exist id: secret-presence run: | - [ ! -z "${{ secrets.ORG_VERACODE_API_ID }}" ] && echo "::set-output name=ORG_VERACODE_API_ID::true" - [ ! -z "${{ secrets.ORG_VERACODE_API_KEY }}" ] && echo "::set-output name=ORG_VERACODE_API_KEY::true" + [ ! -z "${{ secrets.ORG_VERACODE_API_ID }}" ] && echo "ORG_VERACODE_API_ID=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.ORG_VERACODE_API_KEY }}" ] && echo "ORG_VERACODE_API_KEY=true" >> $GITHUB_OUTPUT exit 0 verify-formatting: diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index d9dda3844..a16da4681 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -52,7 +52,7 @@ jobs: - name: Check whether secrets exist id: secret-presence run: | - [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "::set-output name=SONAR_TOKEN::true" + [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "SONAR_TOKEN=true" >> $GITHUB_OUTPUT exit 0 verify-formatting: From 31bfed751c10b8df5063ed7b06720c2f1cb0bf87 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Wed, 19 Apr 2023 08:57:06 +0200 Subject: [PATCH 088/132] fix: only run trivy when docker images were actually built (#240) * fix: run trivy only if image exists * update checks --- .github/workflows/trivy.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 819fec089..67ab0fb12 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -74,8 +74,17 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3.3.0 + + ## This step will fail if the docker images is not found + - name: "Check if image exists" + id: imageCheck + run: | + docker manifest inspect tractusx/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }} + continue-on-error: true + + ## the next two steps will only execute if the image exists check was successful - name: Run Trivy vulnerability scanner - if: always() + if: success() && steps.imageCheck.outcome != 'failure' uses: aquasecurity/trivy-action@master with: image-ref: "tractusx/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }}" @@ -85,7 +94,7 @@ jobs: severity: "CRITICAL,HIGH" timeout: "10m0s" - name: Upload Trivy scan results to GitHub Security tab - if: always() + if: success() && steps.imageCheck.outcome != 'failure' uses: github/codeql-action/upload-sarif@v2 with: sarif_file: "trivy-results-${{ matrix.image }}.sarif" From 6521dc37230c2fbc2a347f60feca481d443ee610 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" <100704677+tuncaytunc-zf@users.noreply.github.com> Date: Wed, 19 Apr 2023 13:02:21 +0200 Subject: [PATCH 089/132] refactor: Extract the setup-java action into a re-usable action (#246) * Extract the checkout and setup-java action into a re-usable action * Commit actions. * fix action * remove checkout extraction --- .../actions/publish-docker-image/action.yml | 10 +-- .../actions/run-deployment-test/action.yml | 10 +-- .github/actions/setup-java/action.yml | 32 ++++++++++ .github/workflows/build.yaml | 26 ++------ .github/workflows/business-tests.yaml | 8 +-- .github/workflows/deployment-test.yaml | 3 +- .github/workflows/draft-new-release.yaml | 7 +-- .github/workflows/helm-chart-release.yaml | 3 +- .github/workflows/helm-lint.yaml | 1 - .github/workflows/publish-docker.yaml | 6 +- .github/workflows/publish-new-release.yml | 17 +---- .github/workflows/trivy.yml | 6 +- .github/workflows/veracode.yaml | 30 ++------- .github/workflows/verify.yaml | 62 ++++--------------- 14 files changed, 71 insertions(+), 150 deletions(-) create mode 100644 .github/actions/setup-java/action.yml diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 206e13d4c..2f8a8c522 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -41,8 +41,7 @@ inputs: runs: using: "composite" steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 ##################### # Login to DockerHub @@ -56,12 +55,7 @@ runs: ##################### # Build JAR file ##################### - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Build Controlplane shell: bash run: |- diff --git a/.github/actions/run-deployment-test/action.yml b/.github/actions/run-deployment-test/action.yml index ed720b4be..9f4b40d58 100644 --- a/.github/actions/run-deployment-test/action.yml +++ b/.github/actions/run-deployment-test/action.yml @@ -42,8 +42,7 @@ inputs: runs: using: "composite" steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - name: Cache ContainerD Image Layers uses: actions/cache@v3 @@ -51,12 +50,7 @@ runs: path: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs key: ${{ runner.os }}-io.containerd.snapshotter.v1.overlayfs - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Build docker images shell: bash diff --git a/.github/actions/setup-java/action.yml b/.github/actions/setup-java/action.yml new file mode 100644 index 000000000..ed03fafb3 --- /dev/null +++ b/.github/actions/setup-java/action.yml @@ -0,0 +1,32 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +name: "Setup JDK 17" +description: "Setup JDK 17" +runs: + using: "composite" + steps: + - name: Setup JDK 17 + uses: actions/setup-java@v3.11.0 + with: + java-version: '17' + distribution: 'temurin' + cache: 'gradle' \ No newline at end of file diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a701ae362..0713b0857 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -71,14 +71,8 @@ jobs: needs: [ secret-presence ] steps: # Set-Up - - name: Checkout - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/setup-java # Build - name: Build Extensions run: |- @@ -104,8 +98,7 @@ jobs: permissions: contents: write steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/publish-docker-image with: rootDir: edc-controlplane/${{ matrix.name }} @@ -128,8 +121,7 @@ jobs: permissions: contents: write steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/publish-docker-image with: rootDir: edc-dataplane/${{ matrix.name }} @@ -149,15 +141,9 @@ jobs: needs.secret-presence.outputs.GPG_PASSPHRASE && needs.secret-presence.outputs.GPG_PRIVATE_KEY && github.event_name != 'pull_request' && github.ref != 'refs/heads/releases' steps: # Set-Up - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Import GPG Key uses: crazy-max/ghaction-import-gpg@v5 with: diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 39caaadb1..a4726a443 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -50,15 +50,9 @@ jobs: ### Set-Up ### ############## - - name: Checkout uses: actions/checkout@v3.3.0 - - name: Set-Up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + uses: ./.github/actions/setup-java - name: Cache ContainerD Image Layers uses: actions/cache@v3 diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index 7d38b24ac..8e75ae31e 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -47,8 +47,7 @@ jobs: deployment-test-memory: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/run-deployment-test name: "Run deployment test using KinD and Helm" with: diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 22222ba8a..98e3c956a 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -33,12 +33,7 @@ jobs: git config user.name "GitHub actions" git config user.email noreply@github.com - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + uses: ./.github/actions/setup-java - name: Bump version in gradle.properties run: |- diff --git a/.github/workflows/helm-chart-release.yaml b/.github/workflows/helm-chart-release.yaml index bd5e55302..f19c841b9 100644 --- a/.github/workflows/helm-chart-release.yaml +++ b/.github/workflows/helm-chart-release.yaml @@ -38,8 +38,7 @@ jobs: steps: # fetch-depth: 0 is required to determine differences in chart(s) - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 with: fetch-depth: 0 diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 7640d0a38..0b5a70f1f 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -26,7 +26,6 @@ jobs: ### Set-Up ### ############## - - name: Checkout uses: actions/checkout@v3.3.0 with: fetch-depth: 0 diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index 794d15061..24aaf2ff4 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -50,8 +50,7 @@ jobs: contents: write packages: write steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/publish-docker-image with: rootDir: edc-controlplane/${{ matrix.name }} @@ -74,8 +73,7 @@ jobs: contents: write packages: write steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/publish-docker-image with: rootDir: edc-dataplane/${{ matrix.name }} diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 06ee58b61..b7de21257 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -54,15 +54,9 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - name: Checkout uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + uses: ./.github/actions/setup-java - name: Import GPG Key uses: crazy-max/ghaction-import-gpg@v5 @@ -96,7 +90,6 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - name: Checkout uses: actions/checkout@v3.3.0 with: fetch-depth: 0 @@ -144,7 +137,6 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - name: Checkout uses: actions/checkout@v3.3.0 with: # 0 to fetch the full history due to upcoming merge of releases into main branch @@ -177,12 +169,7 @@ jobs: draft: false prerelease: false - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + uses: ./.github/actions/setup-java - name: Merge releases back into main and set new snapshot version if: github.event.pull_request.base.ref == 'releases' diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 67ab0fb12..2fe44c399 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -36,8 +36,7 @@ jobs: contents: read security-events: write steps: - - name: Checkout repository - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - name: Run Trivy vulnerability scanner in repo mode uses: aquasecurity/trivy-action@master with: @@ -72,8 +71,7 @@ jobs: - edc-dataplane-azure-vault - edc-dataplane-hashicorp-vault steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 ## This step will fail if the docker images is not found - name: "Check if image exists" diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index c3e7cb7a1..486c53096 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -23,16 +23,10 @@ jobs: verify-formatting: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 with: fetch-depth: 0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Verify proper formatting run: ./gradlew spotlessCheck @@ -51,14 +45,8 @@ jobs: - edc-controlplane-postgresql-hashicorp-vault steps: # Set-Up - - name: Checkout - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/setup-java # Build - name: Build Controlplane run: |- @@ -95,14 +83,8 @@ jobs: - edc-dataplane-hashicorp-vault steps: # Set-Up - - name: Checkout - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/setup-java # Build - name: Build Dataplane run: |- diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index a16da4681..2cd0432f8 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -58,15 +58,9 @@ jobs: verify-formatting: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Verify proper formatting run: ./gradlew spotlessCheck @@ -78,7 +72,7 @@ jobs: markdown-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 - name: Install mardkdownlint run: npm install -g markdownlint-cli2 @@ -91,15 +85,9 @@ jobs: runs-on: ubuntu-latest needs: [ verify-formatting ] steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Run Unit tests run: ./gradlew test @@ -108,15 +96,9 @@ jobs: runs-on: ubuntu-latest needs: [ verify-formatting ] steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Run Integration tests run: ./gradlew test -DincludeTags="ComponentTest" @@ -125,15 +107,9 @@ jobs: runs-on: ubuntu-latest needs: [ verify-formatting ] steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Run API tests run: ./gradlew test -DincludeTags="ApiTest" @@ -142,15 +118,9 @@ jobs: runs-on: ubuntu-latest needs: [ verify-formatting ] steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Run E2E tests run: ./gradlew :edc-tests:runtime:build test -DincludeTags="EndToEndTest" @@ -162,16 +132,10 @@ jobs: runs-on: ubuntu-latest steps: # Set-Up - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 with: fetch-depth: 0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Cache SonarCloud packages uses: actions/cache@v3 with: From 04985e0476515561df7b61412fc99c1422c6cccf Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Wed, 19 Apr 2023 14:39:29 +0200 Subject: [PATCH 090/132] feat(BusinessPartnerValidation): adds logging if it's enabled on contract agreement validation (#245) * feat(BusinessPartnerValidation): adds logging if it's enabled on contract agreement validation * feat(BusinessPartnerValidation): adds logging on tests * feat(BusinessPartnerValidation): enabled by default on charts config * pr remarks --- .github/workflows/business-tests.yaml | 2 + .../templates/deployment-runtime.yaml | 6 + charts/tractusx-connector-memory/values.yaml | 3 + .../templates/deployment-controlplane.yaml | 6 + charts/tractusx-connector/values.yaml | 3 + .../build.gradle.kts | 2 +- .../BusinessPartnerValidationExtension.java | 126 +++++---- .../AbstractBusinessPartnerValidation.java | 231 +++++++++-------- .../BusinessPartnerDutyFunction.java | 20 +- .../BusinessPartnerPermissionFunction.java | 22 +- .../BusinessPartnerProhibitionFunction.java | 22 +- ...usinessPartnerValidationExtensionTest.java | 23 ++ ...AbstractBusinessPartnerValidationTest.java | 239 ++++++++++-------- .../edc/lifecycle/MultiRuntimeTest.java | 2 + .../tractusx/edc/lifecycle/Participant.java | 4 + .../tests/HttpConsumerPullWithProxyTest.java | 6 +- settings.gradle.kts | 36 +-- 17 files changed, 439 insertions(+), 314 deletions(-) diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index a4726a443..cbf0f3767 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -179,6 +179,7 @@ jobs: --set dataplane.image.repository=docker.io/library/edc-dataplane-hashicorp-vault \ --set controlplane.debug.enabled=true \ --set controlplane.suspendOnStart=false \ + --set controlplane.businesspartnervalidation.log.agreement.validation=true \ --set postgresql.enabled=true \ --set postgresql.username=user \ --set postgresql.password=password \ @@ -212,6 +213,7 @@ jobs: --set dataplane.image.repository=docker.io/library/edc-dataplane-hashicorp-vault \ --set controlplane.debug.enabled=true \ --set controlplane.suspendOnStart=false \ + --set controlplane.businesspartnervalidation.log.agreement.validation=true \ --set postgresql.enabled=true \ --set postgresql.username=user \ --set postgresql.password=password \ diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml index 04386678c..3e7bd89e3 100644 --- a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -253,6 +253,12 @@ spec: value: "0" - name: "EDC_CP_ADAPTER_REUSE_CONTRACT_AGREEMENT" value: "0" + + ########################### + ## BUSINESS PARTNER NUMBER VALIDATION EXTENSION ## + ########################### + - name: "TRACTUSX_BUSINESSPARTNERVALIDATION_LOG_AGREEMENT_VALIDATION" + value: {{ .Values.runtime.businessPartnerValidation.log.agreementValidation | quote }} ###################################### ## Additional environment variables ## diff --git a/charts/tractusx-connector-memory/values.yaml b/charts/tractusx-connector-memory/values.yaml index 66fa1b7fe..83ce92818 100644 --- a/charts/tractusx-connector-memory/values.yaml +++ b/charts/tractusx-connector-memory/values.yaml @@ -121,6 +121,9 @@ runtime: public: port: 8086 path: /api/public + businessPartnerValidation: + log: + agreementValidation: true service: # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. type: ClusterIP diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 6eded494c..daab957e4 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -331,6 +331,12 @@ spec: - name: "EDC_CP_ADAPTER_REUSE_CONTRACT_AGREEMENT" value: "0" + ########################### + ## BUSINESS PARTNER NUMBER VALIDATION EXTENSION ## + ########################### + - name: "TRACTUSX_BUSINESSPARTNERVALIDATION_LOG_AGREEMENT_VALIDATION" + value: {{ .Values.controlplane.businessPartnerValidation.log.agreementValidation | quote }} + ###################################### ## Additional environment variables ## ###################################### diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index aebd45481..21acfc20b 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -122,6 +122,9 @@ controlplane: path: /observability # -- allow or disallow insecure access, i.e. access without authentication insecure: true + businessPartnerValidation: + log: + agreementValidation: true service: # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. type: ClusterIP diff --git a/edc-extensions/business-partner-validation/build.gradle.kts b/edc-extensions/business-partner-validation/build.gradle.kts index 53cb11e31..198886d9a 100644 --- a/edc-extensions/business-partner-validation/build.gradle.kts +++ b/edc-extensions/business-partner-validation/build.gradle.kts @@ -1,4 +1,3 @@ - plugins { `java-library` `maven-publish` @@ -7,5 +6,6 @@ plugins { dependencies { api(edc.spi.core) implementation(edc.spi.policy) + implementation(edc.spi.contract) implementation(edc.spi.policyengine) } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java index ee076406f..d88293a72 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -26,6 +26,7 @@ import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.policy.model.Prohibition; import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; @@ -37,60 +38,73 @@ public class BusinessPartnerValidationExtension implements ServiceExtension { - /** - * The key for business partner numbers constraints. Must be used as left operand when declaring - * constraints. - * - *

Example: - * - *

-   * {
-   *     "constraint": {
-   *         "leftOperand": "BusinessPartnerNumber",
-   *         "operator": "EQ",
-   *         "rightOperand": "BPNLCDQ90000X42KU"
-   *     }
-   * }
-   * 
- */ - public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; - - public BusinessPartnerValidationExtension() {} - - public BusinessPartnerValidationExtension( - final RuleBindingRegistry ruleBindingRegistry, final PolicyEngine policyEngine) { - this.ruleBindingRegistry = ruleBindingRegistry; - this.policyEngine = policyEngine; - } - - @Inject private RuleBindingRegistry ruleBindingRegistry; - - @Inject private PolicyEngine policyEngine; - - @Override - public String name() { - return "Business Partner Validation Extension"; - } - - @Override - public void initialize(ServiceExtensionContext context) { - - final Monitor monitor = context.getMonitor(); - - final BusinessPartnerDutyFunction dutyFunction = new BusinessPartnerDutyFunction(monitor); - final BusinessPartnerPermissionFunction permissionFunction = - new BusinessPartnerPermissionFunction(monitor); - final BusinessPartnerProhibitionFunction prohibitionFunction = - new BusinessPartnerProhibitionFunction(monitor); - - ruleBindingRegistry.bind("USE", ALL_SCOPES); - ruleBindingRegistry.bind(BUSINESS_PARTNER_CONSTRAINT_KEY, ALL_SCOPES); - - policyEngine.registerFunction( - ALL_SCOPES, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); - policyEngine.registerFunction( - ALL_SCOPES, Permission.class, BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); - policyEngine.registerFunction( - ALL_SCOPES, Prohibition.class, BUSINESS_PARTNER_CONSTRAINT_KEY, prohibitionFunction); - } + /** + * The key for business partner numbers constraints. Must be used as left operand when declaring + * constraints. + * + *

Example: + * + *

+     * {
+     *     "constraint": {
+     *         "leftOperand": "BusinessPartnerNumber",
+     *         "operator": "EQ",
+     *         "rightOperand": "BPNLCDQ90000X42KU"
+     *     }
+     * }
+     * 
+ */ + public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; + + public static final String DEFAULT_LOG_AGREEMENT_EVALUATION = "true"; + + + @Setting(value = "Enable logging when evaluating the business partner constraints in the agreement validation", type = "boolean", defaultValue = DEFAULT_LOG_AGREEMENT_EVALUATION) + public static final String BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION = "tractusx.businesspartnervalidation.log.agreement.validation"; + @Inject + private RuleBindingRegistry ruleBindingRegistry; + @Inject + private PolicyEngine policyEngine; + + public BusinessPartnerValidationExtension() { + } + + public BusinessPartnerValidationExtension( + final RuleBindingRegistry ruleBindingRegistry, final PolicyEngine policyEngine) { + this.ruleBindingRegistry = ruleBindingRegistry; + this.policyEngine = policyEngine; + } + + @Override + public String name() { + return "Business Partner Validation Extension"; + } + + @Override + public void initialize(ServiceExtensionContext context) { + + final Monitor monitor = context.getMonitor(); + + var logAgreementEvaluation = logAgreementEvaluationSetting(context); + + final BusinessPartnerDutyFunction dutyFunction = new BusinessPartnerDutyFunction(monitor, logAgreementEvaluation); + final BusinessPartnerPermissionFunction permissionFunction = + new BusinessPartnerPermissionFunction(monitor, logAgreementEvaluation); + final BusinessPartnerProhibitionFunction prohibitionFunction = + new BusinessPartnerProhibitionFunction(monitor, logAgreementEvaluation); + + ruleBindingRegistry.bind("USE", ALL_SCOPES); + ruleBindingRegistry.bind(BUSINESS_PARTNER_CONSTRAINT_KEY, ALL_SCOPES); + + policyEngine.registerFunction( + ALL_SCOPES, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); + policyEngine.registerFunction( + ALL_SCOPES, Permission.class, BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); + policyEngine.registerFunction( + ALL_SCOPES, Prohibition.class, BUSINESS_PARTNER_CONSTRAINT_KEY, prohibitionFunction); + } + + private Boolean logAgreementEvaluationSetting(ServiceExtensionContext context) { + return Boolean.parseBoolean(context.getSetting(BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION, DEFAULT_LOG_AGREEMENT_EVALUATION)); + } } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java index 55cb0d52b..ecb5b81ef 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -20,132 +20,147 @@ package org.eclipse.tractusx.edc.validation.businesspartner.functions; -import java.util.Map; -import java.util.Objects; +import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreement; import org.eclipse.edc.policy.engine.spi.PolicyContext; import org.eclipse.edc.policy.model.Operator; import org.eclipse.edc.spi.agent.ParticipantAgent; import org.eclipse.edc.spi.monitor.Monitor; +import java.util.Map; +import java.util.Objects; + +import static java.lang.String.format; + /** * Abstract class for BusinessPartnerNumber validation. This class may be inherited from the EDC * policy enforcing functions for duties, permissions and prohibitions. */ public abstract class AbstractBusinessPartnerValidation { - // Developer Note: - // Problems reported to the policy context are not logged. Therefore, everything - // that is reported to the policy context should be logged, too. - - private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING = - "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'EQ' right value must be of type 'String'. Unsupported type: '%s'"; - private static final String FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR = - "Failing evaluation because of invalid BusinessPartnerNumber constraint. As operator only 'EQ' is supported. Unsupported operator: '%s'"; - - private final Monitor monitor; - - protected AbstractBusinessPartnerValidation(Monitor monitor) { - this.monitor = Objects.requireNonNull(monitor); - } - - /** - * Name of the claim that contains the Business Partner Number. - * - *

Please note: At the time of writing (April 2022) the business partner - * number is part of the 'referringConnector' claim in the IDS DAT token. This will probably - * change for the next release. - */ - private static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; - - /** - * Evaluation funtion to decide whether a claim belongs to a specific business partner. - * - * @param operator operator of the constraint - * @param rightValue right value fo the constraint, that contains the business partner number - * (e.g. BPNLCDQ90000X42KU) - * @param policyContext context of the policy with claims - * @return true if claims are from the constrained business partner - */ - protected boolean evaluate( - final Operator operator, final Object rightValue, final PolicyContext policyContext) { - - if (policyContext.hasProblems() && !policyContext.getProblems().isEmpty()) { - String problems = String.join(", ", policyContext.getProblems()); - String message = - String.format( - "BusinessPartnerNumberValidation: Rejecting PolicyContext with problems. Problems: %s", - problems); - monitor.debug(message); - return false; + // Developer Note: + // Problems reported to the policy context are not logged. Therefore, everything + // that is reported to the policy context should be logged, too. + + private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'EQ' right value must be of type 'String'. Unsupported type: '%s'"; + private static final String FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. As operator only 'EQ' is supported. Unsupported operator: '%s'"; + /** + * Name of the claim that contains the Business Partner Number. + * + *

Please note: At the time of writing (April 2022) the business partner + * number is part of the 'referringConnector' claim in the IDS DAT token. This will probably + * change for the next release. + */ + private static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; + private final Monitor monitor; + private final boolean logAgreementEvaluation; + + protected AbstractBusinessPartnerValidation(Monitor monitor, boolean logAgreementEvaluation) { + this.monitor = Objects.requireNonNull(monitor); + this.logAgreementEvaluation = logAgreementEvaluation; } - final ParticipantAgent participantAgent = policyContext.getParticipantAgent(); - final Map claims = participantAgent.getClaims(); - - if (!claims.containsKey(REFERRING_CONNECTOR_CLAIM)) { - return false; + /** + * At the time of writing (11. April 2022) the business partner number is part of the + * 'referringConnector' claim, which contains a connector URL. As the CX projects are not further + * aligned about the URL formatting, the enforcement can only be done by checking whether the URL + * _contains_ the number. As this introduces some insecurities when validation business partner + * numbers, this should be addresses in the long term. + * + * @param referringConnectorClaim describing URL with business partner number + * @param businessPartnerNumber of the constraint + * @return true if claim contains the business partner number + */ + private static boolean isCorrectBusinessPartner( + String referringConnectorClaim, String businessPartnerNumber) { + return referringConnectorClaim.contains(businessPartnerNumber); } - Object referringConnectorClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); - String referringConnectorClaim = null; - - if (referringConnectorClaimObject instanceof String) { - referringConnectorClaim = (String) referringConnectorClaimObject; + public boolean isLogAgreementEvaluation() { + return logAgreementEvaluation; } - if (referringConnectorClaim == null || referringConnectorClaim.isEmpty()) { - return false; + /** + * Evaluation funtion to decide whether a claim belongs to a specific business partner. + * + * @param operator operator of the constraint + * @param rightValue right value fo the constraint, that contains the business partner number + * (e.g. BPNLCDQ90000X42KU) + * @param policyContext context of the policy with claims + * @return true if claims are from the constrained business partner + */ + protected boolean evaluate( + final Operator operator, final Object rightValue, final PolicyContext policyContext) { + + if (policyContext.hasProblems() && !policyContext.getProblems().isEmpty()) { + String problems = String.join(", ", policyContext.getProblems()); + String message = + format( + "BusinessPartnerNumberValidation: Rejecting PolicyContext with problems. Problems: %s", + problems); + monitor.debug(message); + return false; + } + + final ParticipantAgent participantAgent = policyContext.getParticipantAgent(); + final Map claims = participantAgent.getClaims(); + + if (!claims.containsKey(REFERRING_CONNECTOR_CLAIM)) { + return false; + } + + Object referringConnectorClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); + String referringConnectorClaim = null; + + if (referringConnectorClaimObject instanceof String) { + referringConnectorClaim = (String) referringConnectorClaimObject; + } + + if (referringConnectorClaim == null || referringConnectorClaim.isEmpty()) { + return false; + } + + if (operator == Operator.EQ) { + return isBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); + } else { + final String message = format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR, operator); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } } - if (operator == Operator.EQ) { - return isBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); - } else { - final String message = String.format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR, operator); - monitor.warning(message); - policyContext.reportProblem(message); - return false; - } - } - - /** - * @param referringConnectorClaim of the participant - * @param businessPartnerNumber object - * @return true if object is string and successfully evaluated against the claim - */ - private boolean isBusinessPartnerNumber( - String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { - if (businessPartnerNumber == null) { - final String message = String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); - monitor.warning(message); - policyContext.reportProblem(message); - return false; + /** + * @param referringConnectorClaim of the participant + * @param businessPartnerNumber object + * @return true if object is string and successfully evaluated against the claim + */ + private boolean isBusinessPartnerNumber( + String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { + if (businessPartnerNumber == null) { + final String message = format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } + if (!(businessPartnerNumber instanceof String)) { + final String message = + format( + FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, + businessPartnerNumber.getClass().getName()); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } + + var businessPartnerNumberStr = (String) businessPartnerNumber; + var agreement = policyContext.getContextData(ContractAgreement.class); + var isCorrectBusinessPartner = isCorrectBusinessPartner(referringConnectorClaim, businessPartnerNumberStr); + + if (agreement != null && logAgreementEvaluation) { + monitor.info(format("Evaluated policy access for referringConnectorClaim: %s and contract id: %s with result: %s", referringConnectorClaim, agreement.getId(), isCorrectBusinessPartner)); + } + return isCorrectBusinessPartner; } - if (!(businessPartnerNumber instanceof String)) { - final String message = - String.format( - FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, - businessPartnerNumber.getClass().getName()); - monitor.warning(message); - policyContext.reportProblem(message); - return false; - } - - return isCorrectBusinessPartner(referringConnectorClaim, (String) businessPartnerNumber); - } - - /** - * At the time of writing (11. April 2022) the business partner number is part of the - * 'referringConnector' claim, which contains a connector URL. As the CX projects are not further - * aligned about the URL formatting, the enforcement can only be done by checking whether the URL - * _contains_ the number. As this introduces some insecurities when validation business partner - * numbers, this should be addresses in the long term. - * - * @param referringConnectorClaim describing URL with business partner number - * @param businessPartnerNumber of the constraint - * @return true if claim contains the business partner number - */ - private static boolean isCorrectBusinessPartner( - String referringConnectorClaim, String businessPartnerNumber) { - return referringConnectorClaim.contains(businessPartnerNumber); - } } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java index f53ba3cbc..061d7fd7d 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java @@ -26,16 +26,18 @@ import org.eclipse.edc.policy.model.Operator; import org.eclipse.edc.spi.monitor.Monitor; -/** AtomicConstraintFunction to validate business partner numbers for edc duties. */ +/** + * AtomicConstraintFunction to validate business partner numbers for edc duties. + */ public class BusinessPartnerDutyFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { + implements AtomicConstraintFunction { - public BusinessPartnerDutyFunction(Monitor monitor) { - super(monitor); - } + public BusinessPartnerDutyFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { + super(monitor, shouldLogOnAgreementEvaluation); + } - @Override - public boolean evaluate(Operator operator, Object rightValue, Duty rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } + @Override + public boolean evaluate(Operator operator, Object rightValue, Duty rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java index 07bda765e..b6713c477 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java @@ -26,17 +26,19 @@ import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.spi.monitor.Monitor; -/** AtomicConstraintFunction to validate business partner numbers for edc permissions. */ +/** + * AtomicConstraintFunction to validate business partner numbers for edc permissions. + */ public class BusinessPartnerPermissionFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { + implements AtomicConstraintFunction { - public BusinessPartnerPermissionFunction(Monitor monitor) { - super(monitor); - } + public BusinessPartnerPermissionFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { + super(monitor, shouldLogOnAgreementEvaluation); + } - @Override - public boolean evaluate( - Operator operator, Object rightValue, Permission rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } + @Override + public boolean evaluate( + Operator operator, Object rightValue, Permission rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java index f3cddf9fe..79e318741 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java @@ -26,17 +26,19 @@ import org.eclipse.edc.policy.model.Prohibition; import org.eclipse.edc.spi.monitor.Monitor; -/** AtomicConstraintFunction to validate business partner numbers for edc prohibitions. */ +/** + * AtomicConstraintFunction to validate business partner numbers for edc prohibitions. + */ public class BusinessPartnerProhibitionFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { + implements AtomicConstraintFunction { - public BusinessPartnerProhibitionFunction(Monitor monitor) { - super(monitor); - } + public BusinessPartnerProhibitionFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { + super(monitor, shouldLogOnAgreementEvaluation); + } - @Override - public boolean evaluate( - Operator operator, Object rightValue, Prohibition rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } + @Override + public boolean evaluate( + Operator operator, Object rightValue, Prohibition rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } } diff --git a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java index 0240dc9ef..dcea3be41 100644 --- a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java @@ -27,10 +27,13 @@ import org.eclipse.edc.policy.model.Prohibition; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerPermissionFunction; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -105,4 +108,24 @@ void testRegisterProhibitionFunction() { eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), any()); } + + @Test + void testLogConfiguration() { + + when(serviceExtensionContext.getSetting(BusinessPartnerValidationExtension.BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION, "true")).thenReturn("false"); + + var captor = ArgumentCaptor.forClass(BusinessPartnerPermissionFunction.class); + // invoke + extension.initialize(serviceExtensionContext); + + // verify + verify(policyEngine) + .registerFunction( + anyString(), + eq(Permission.class), + eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), + captor.capture()); + + assertThat(captor.getValue().isLogAgreementEvaluation()).isFalse(); + } } diff --git a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java index e8909c04e..2bc0738b0 100644 --- a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java @@ -20,10 +20,10 @@ package org.eclipse.tractusx.edc.validation.businesspartner.functions; -import java.util.Collections; -import java.util.List; +import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreement; import org.eclipse.edc.policy.engine.spi.PolicyContext; import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.agent.ParticipantAgent; import org.eclipse.edc.spi.monitor.Monitor; import org.junit.jupiter.api.Assertions; @@ -31,143 +31,180 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; + class AbstractBusinessPartnerValidationTest { - private AbstractBusinessPartnerValidation validation; + private AbstractBusinessPartnerValidation validation; + + // mocks + private Monitor monitor; + private PolicyContext policyContext; + private ParticipantAgent participantAgent; + + @BeforeEach + void BeforeEach() { + this.monitor = Mockito.mock(Monitor.class); + this.policyContext = Mockito.mock(PolicyContext.class); + this.participantAgent = Mockito.mock(ParticipantAgent.class); + + Mockito.when(policyContext.getParticipantAgent()).thenReturn(participantAgent); + + validation = new AbstractBusinessPartnerValidation(monitor, true) { + }; + } + + @ParameterizedTest + @EnumSource(Operator.class) + void testFailsOnUnsupportedOperations(Operator operator) { - // mocks - private Monitor monitor; - private PolicyContext policyContext; - private ParticipantAgent participantAgent; + if (operator == Operator.EQ) { // only allowed operator + return; + } - @BeforeEach - void BeforeEach() { - this.monitor = Mockito.mock(Monitor.class); - this.policyContext = Mockito.mock(PolicyContext.class); - this.participantAgent = Mockito.mock(ParticipantAgent.class); + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("yes"); - Mockito.when(policyContext.getParticipantAgent()).thenReturn(participantAgent); + // invoke & assert + Assertions.assertFalse(validation.evaluate(operator, "foo", policyContext)); + } + + @Test + void testFailsOnUnsupportedRightValue() { + + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("yes"); + + // invoke & assert + Assertions.assertFalse(validation.evaluate(Operator.EQ, 1, policyContext)); + } + + @Test + void testValidationFailsWhenClaimMissing() { - validation = new AbstractBusinessPartnerValidation(monitor) {}; - } + // prepare + prepareContextProblems(null); - @ParameterizedTest - @EnumSource(Operator.class) - void testFailsOnUnsupportedOperations(Operator operator) { + // invoke + final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); - if (operator == Operator.EQ) { // only allowed operator - return; + // assert + Assertions.assertFalse(isValid); } - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("yes"); + @Test + void testValidationSucceedsWhenClaimContainsValue() { - // invoke & assert - Assertions.assertFalse(validation.evaluate(operator, "foo", policyContext)); - } + // prepare + prepareContextProblems(null); - @Test - void testFailsOnUnsupportedRightValue() { + // prepare equals + prepareBusinessPartnerClaim("foo"); + final boolean isEqualsTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("yes"); + // prepare contains + prepareBusinessPartnerClaim("foobar"); + final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - // invoke & assert - Assertions.assertFalse(validation.evaluate(Operator.EQ, 1, policyContext)); - } + // assert + Assertions.assertTrue(isEqualsTrue); + Assertions.assertTrue(isContainedTrue); + } - @Test - void testValidationFailsWhenClaimMissing() { + @Test + void testValidationWhenParticipantHasProblems() { - // prepare - prepareContextProblems(null); + // prepare + prepareContextProblems(Collections.singletonList("big problem")); + prepareBusinessPartnerClaim("foo"); - // invoke - final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); + // invoke + final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); - // assert - Assertions.assertFalse(isValid); - } + // Mockito.verify(monitor.debug(Mockito.anyString()); + Assertions.assertFalse(isValid); + } - @Test - void testValidationSucceedsWhenClaimContainsValue() { + @Test + void testValidationWhenSingleParticipantIsValid() { - // prepare - prepareContextProblems(null); + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("foo"); - // prepare equals - prepareBusinessPartnerClaim("foo"); - final boolean isEqualsTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + // invoke + final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - // prepare contains - prepareBusinessPartnerClaim("foobar"); - final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + // Mockito.verify(monitor.debug(Mockito.anyString()); + Assertions.assertTrue(isContainedTrue); + } - // assert - Assertions.assertTrue(isEqualsTrue); - Assertions.assertTrue(isContainedTrue); - } + @Test + void testValidationWhenSingleParticipantIsValidWithAgreement() { - @Test - void testValidationWhenParticipantHasProblems() { + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("foo"); - // prepare - prepareContextProblems(Collections.singletonList("big problem")); - prepareBusinessPartnerClaim("foo"); + var captor = ArgumentCaptor.forClass(String.class); - // invoke - final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); + var agreement = ContractAgreement.Builder.newInstance() + .id("agreementId") + .providerAgentId("provider") + .consumerAgentId("consumer") + .assetId("assetId") + .policy(Policy.Builder.newInstance().build()) + .build(); - // Mockito.verify(monitor.debug(Mockito.anyString()); - Assertions.assertFalse(isValid); - } + Mockito.when(policyContext.getContextData(eq(ContractAgreement.class))).thenReturn(agreement); - @Test - void testValidationWhenSingleParticipantIsValid() { + // invoke + final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("foo"); + Assertions.assertTrue(isContainedTrue); - // invoke - final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + Mockito.verify(monitor).info(captor.capture()); - // Mockito.verify(monitor.debug(Mockito.anyString()); - Assertions.assertTrue(isContainedTrue); - } + assertThat(captor.getValue()).contains(agreement.getId()).contains("foo"); + } - // In the past it was possible to use the 'IN' constraint with multiple BPNs as - // a list. This is no longer supported. - // The EDC must now always decline this kind of BPN format. - @Test - void testValidationForMultipleParticipants() { + // In the past it was possible to use the 'IN' constraint with multiple BPNs as + // a list. This is no longer supported. + // The EDC must now always decline this kind of BPN format. + @Test + void testValidationForMultipleParticipants() { - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("foo"); + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("foo"); - // invoke & verify - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("foo", "bar"), policyContext)); - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of(1, "foo"), policyContext)); - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("bar", "bar"), policyContext)); - } + // invoke & verify + Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("foo", "bar"), policyContext)); + Assertions.assertFalse(validation.evaluate(Operator.IN, List.of(1, "foo"), policyContext)); + Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("bar", "bar"), policyContext)); + } - private void prepareContextProblems(List problems) { - Mockito.when(policyContext.getProblems()).thenReturn(problems); + private void prepareContextProblems(List problems) { + Mockito.when(policyContext.getProblems()).thenReturn(problems); - if (problems == null || problems.isEmpty()) { - Mockito.when(policyContext.hasProblems()).thenReturn(false); - } else { - Mockito.when(policyContext.hasProblems()).thenReturn(true); + if (problems == null || problems.isEmpty()) { + Mockito.when(policyContext.hasProblems()).thenReturn(false); + } else { + Mockito.when(policyContext.hasProblems()).thenReturn(true); + } } - } - private void prepareBusinessPartnerClaim(String businessPartnerNumber) { - Mockito.when(participantAgent.getClaims()) - .thenReturn(Collections.singletonMap("referringConnector", businessPartnerNumber)); - } + private void prepareBusinessPartnerClaim(String businessPartnerNumber) { + Mockito.when(participantAgent.getClaims()) + .thenReturn(Collections.singletonMap("referringConnector", businessPartnerNumber)); + } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java index a28a2610d..5bf2a2417 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java @@ -69,6 +69,7 @@ public class MultiRuntimeTest { put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + SOKRATES_PUBLIC_API_PORT + "/api/public\"}"); put("edc.receiver.http.dynamic.endpoint", "http://localhost:" + SOKRATES_CONNECTOR_PORT + "/api/consumer/datareference"); + put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); } }); @@ -98,6 +99,7 @@ public class MultiRuntimeTest { put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + PLATO_PUBLIC_API_PORT + "/api/public\"}"); + put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); } }); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java index dd43d4f90..bd1546111 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java @@ -26,6 +26,7 @@ import org.eclipse.edc.connector.api.management.transferprocess.model.TransferRequestDto; import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.eclipse.edc.policy.model.PolicyRegistrationTypes; import org.eclipse.edc.spi.asset.AssetSelectorExpression; import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.system.ServiceExtension; @@ -76,6 +77,9 @@ public Participant(String moduleName, String runtimeName, Map pr this.bpn = runtimeName + "-BPN"; this.backend = properties.get("edc.receiver.http.dynamic.endpoint"); this.registerServiceMock(IdentityService.class, new MockDapsService(getBpn())); + + typeManager.registerTypes(PolicyRegistrationTypes.TYPES.toArray(Class[]::new)); + } @Override diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java index 78f3d2013..1f93ae5e7 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java @@ -38,7 +38,7 @@ import static org.awaitility.Awaitility.await; import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; import static org.eclipse.edc.connector.transfer.dataplane.spi.TransferDataPlaneConstants.HTTP_PROXY; -import static org.eclipse.tractusx.edc.policy.PolicyHelperFunctions.noConstraintPolicy; +import static org.eclipse.tractusx.edc.policy.PolicyHelperFunctions.businessPartnerNumberPolicy; @EndToEndTest public class HttpConsumerPullWithProxyTest extends MultiRuntimeTest { @@ -61,8 +61,8 @@ void transferData_privateBackend() throws IOException, InterruptedException { .authKey(authCodeHeaderName) .authCode(authCode) .build()); - plato.createPolicy(noConstraintPolicy("policy-1")); - plato.createPolicy(noConstraintPolicy("policy-2")); + plato.createPolicy(businessPartnerNumberPolicy("policy-1", sokrates.getBpn())); + plato.createPolicy(businessPartnerNumberPolicy("policy-2", sokrates.getBpn())); plato.createContractDefinition(assetId, "def-1", "policy-1", "policy-2", ONE_WEEK); var negotiationId = sokrates.negotiateContract(plato, assetId); diff --git a/settings.gradle.kts b/settings.gradle.kts index e22d4aacc..a4158ef8c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -58,7 +58,7 @@ dependencyResolutionManagement { } // create version catalog for all EDC modules create("edc") { - version("edc", "0.0.1-20230220-SNAPSHOT") + version("edc", "0.0.1-20230220.patch1") library("spi-catalog", "org.eclipse.edc", "catalog-spi").versionRef("edc") library("spi-auth", "org.eclipse.edc", "auth-spi").versionRef("edc") library("spi-transfer", "org.eclipse.edc", "transfer-spi").versionRef("edc") @@ -137,34 +137,38 @@ dependencyResolutionManagement { library("micrometer-jersey", "org.eclipse.edc", "jersey-micrometer").versionRef("edc") library("micrometer-jetty", "org.eclipse.edc", "jetty-micrometer").versionRef("edc") library("monitor-jdklogger", "org.eclipse.edc", "monitor-jdk-logger").versionRef("edc") - library("transfer.dynamicreceiver", "org.eclipse.edc", "transfer-pull-http-dynamic-receiver").versionRef("edc") + library( + "transfer.dynamicreceiver", + "org.eclipse.edc", + "transfer-pull-http-dynamic-receiver" + ).versionRef("edc") library("transfer.receiver", "org.eclipse.edc", "transfer-pull-http-receiver").versionRef("edc") bundle( - "connector", - listOf("boot", "core-connector", "core-jersey", "core-controlplane", "api-observability") + "connector", + listOf("boot", "core-connector", "core-jersey", "core-controlplane", "api-observability") ) bundle( - "dpf", - listOf("dpf-transfer", "dpf-selector-core", "dpf-selector-client", "spi-dataplane-selector") + "dpf", + listOf("dpf-transfer", "dpf-selector-core", "dpf-selector-client", "spi-dataplane-selector") ) bundle( - "sqlstores", - listOf( - "sql-assetindex", - "sql-contract-definition", - "sql-contract-negotiation", - "sql-transferprocess", - "sql-policydef" - ) + "sqlstores", + listOf( + "sql-assetindex", + "sql-contract-definition", + "sql-contract-negotiation", + "sql-transferprocess", + "sql-policydef" + ) ) bundle( - "monitoring", - listOf("micrometer-core", "micrometer-jersey", "micrometer-jetty") + "monitoring", + listOf("micrometer-core", "micrometer-jersey", "micrometer-jetty") // listOf("micrometer-core", "micrometer-jersey", "micrometer-jetty", "monitor-jdklogger") ) } From 463c909d6d120610bcaf41d1464fce7ece152b69 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 19 Apr 2023 17:06:03 +0200 Subject: [PATCH 091/132] release-fix: use correct value --- .github/workflows/publish-new-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index b7de21257..fbd0780ca 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -60,7 +60,7 @@ jobs: - name: Import GPG Key uses: crazy-max/ghaction-import-gpg@v5 - env: + with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} passphrase: ${{ secrets.GPG_PASSPHRASE }} From 98988daf27358ce9a43c857c64cf292b7122ee76 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 17:16:44 +0200 Subject: [PATCH 092/132] Release version 0.3.3 (#249) * Generate OpenApi Spec * feat(baseImage): replace alpine with temurin as base image for running java application * Lint and refactor mostly all *.md files * Lint new changes from develop branch * Replace appearance of product-edc with tractusx-edc * Fix README.md and Transfer Data.md * Fix Transfer Data.md * Regenerate helm chart README.md files * Remove left over html tags from root REAMDE.md * Add empty line at EOF * Update CODE_OF_CONDUCT.md * Retrigger ci * Release: fix version handling * Prepare release 0.3.1 * Cherry-picked upstream commits (QGate stuff) in preparation for the 0.3.1 release * fix: use snapshot version after publish workflow * docs: add additional info for running business tests locally * feat(CI): add Markdown linter * md lint fix * pr remarks * Apply suggestions from code review Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * Update .github/workflows/verify.yaml Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * chore(md-linting): Fix markdown lint * fix: make AZKV clientsecret or certificate mutually exclusive * revert pointless blanks * fix: use correct paths for GH Packages docker reg. * fix: only dockerize if a dockerfile exists * chore: use old repo URL for Maven publication * fix: use PAT to publish to CXNG product-edc repo * PR Remarks * fix: remove duplicated code fragment in CHANGELOG * feat: removed backend service, replaced with JVM runner test moved consumer EDR controller to runtime module * docs: create decision record about renaming git branches * removed obsolete HTTP test * feat(charts): removes edc-controlplane and edc-dataplane charts * Update docs/development/decision-records/2023-04-03_renaming_branches/README.md Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * Update docs/development/decision-records/2023-04-03_renaming_branches/README.md Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * feat(dataEncryption): removes lombok from data-encryption module * Update edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * Fix issue with sql pool * fix: add newline to file * chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump actions/setup-java from 3.10.0 to 3.11.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3.10.0 to 3.11.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3.10.0...v3.11.0) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore(deps): bump alpine Bumps alpine from 3.17.2 to 3.17.3. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * docs: create decision-record about refactoring helm charts * chore(deps): bump crazy-max/ghaction-import-gpg from 1 to 5 Bumps [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) from 1 to 5. - [Release notes](https://github.com/crazy-max/ghaction-import-gpg/releases) - [Changelog](https://github.com/crazy-max/ghaction-import-gpg/blob/v5/CHANGELOG.md) - [Commits](https://github.com/crazy-max/ghaction-import-gpg/compare/v1...v5) --- updated-dependencies: - dependency-name: crazy-max/ghaction-import-gpg dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * chore(deps): bump helm/chart-testing-action from 2.3.1 to 2.4.0 Bumps [helm/chart-testing-action](https://github.com/helm/chart-testing-action) from 2.3.1 to 2.4.0. - [Release notes](https://github.com/helm/chart-testing-action/releases) - [Commits](https://github.com/helm/chart-testing-action/compare/v2.3.1...v2.4.0) --- updated-dependencies: - dependency-name: helm/chart-testing-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore(deps): bump mikefarah/yq from 4.31.2 to 4.33.3 Bumps [mikefarah/yq](https://github.com/mikefarah/yq) from 4.31.2 to 4.33.3. - [Release notes](https://github.com/mikefarah/yq/releases) - [Changelog](https://github.com/mikefarah/yq/blob/master/release_notes.txt) - [Commits](https://github.com/mikefarah/yq/compare/v4.31.2...v4.33.3) --- updated-dependencies: - dependency-name: mikefarah/yq dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * feature: publish docker images to DockerHub * add manual docker-publish workflow * avoid input params, add concurrency * add checkout action * creds as action inputs * add jar build step * make namespace overridable * updated notices * incorporate new docker publish flow * update chart deployment specs * fix formatting * markdown lint * fix workflow * remove image namespace * prevent all interaction with dockerhub on pull requests * docs: add technical committer to pr_etiquette.md (#182) * chore: update to temurin 17 (#212) * chore: update dockerfiles and GH Actions to temurin 17 * pin specific version * feat(tests): removes lombok from edc-tests module (#159) * chore: add a template for pull request descriptions (#213) * fix: Adapt Helm Chart for version 0.3.x (#211) * Adapt Charts for version 0.3.x * fix business-tests * add edc.receiver.http.dynamic.endpoint * fix business-tests * code-review findings * refactor: rename git branches (#218) * refactor: update branch names and references in our documentation * publish packages to tractus-x * chore(deps): bump io.cucumber:cucumber-junit-platform-engine from 7.11.1 to 7.11.2 (#221) * refactor: rename git branches (#218) * refactor: update branch names and references in our documentation * publish packages to tractus-x * chore(deps): bump io.cucumber:cucumber-junit-platform-engine Bumps [io.cucumber:cucumber-junit-platform-engine](https://github.com/cucumber/cucumber-jvm) from 7.11.1 to 7.11.2. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.11.1...v7.11.2) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit-platform-engine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --------- Signed-off-by: dependabot[bot] Co-authored-by: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump io.cucumber:cucumber-java from 7.11.1 to 7.11.2 (#225) Bumps [io.cucumber:cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.11.1 to 7.11.2. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.11.1...v7.11.2) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump org.testcontainers:junit-jupiter from 1.17.6 to 1.18.0 (#224) Bumps [org.testcontainers:junit-jupiter](https://github.com/testcontainers/testcontainers-java) from 1.17.6 to 1.18.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.17.6...1.18.0) --- updated-dependencies: - dependency-name: org.testcontainers:junit-jupiter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump com.bmuschko.docker-remote-api from 9.2.1 to 9.3.1 (#222) Bumps com.bmuschko.docker-remote-api from 9.2.1 to 9.3.1. --- updated-dependencies: - dependency-name: com.bmuschko.docker-remote-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump org.testcontainers:vault from 1.17.6 to 1.18.0 (#223) Bumps [org.testcontainers:vault](https://github.com/testcontainers/testcontainers-java) from 1.17.6 to 1.18.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.17.6...1.18.0) --- updated-dependencies: - dependency-name: org.testcontainers:vault dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> * docs(control-plane-adapter): improve documentation on how to use the control-plane adapter extension (#210) * feature: create in-mem helm chart (#219) * feature: create the tractusx-connector-memory chart * pr remarks * pr remarks * increase waiting for negotiation, sometimes takes longer then 2 seconds * Apply suggestions from code review Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * pr remarks * Update charts/tractusx-connector-memory/templates/deployment-runtime.yaml Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --------- Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * chore(deps): bump org.slf4j:slf4j-api from 2.0.3 to 2.0.7 (#234) Bumps [org.slf4j:slf4j-api](https://github.com/qos-ch/slf4j) from 2.0.3 to 2.0.7. - [Release notes](https://github.com/qos-ch/slf4j/releases) - [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.3...v_2.0.7) --- updated-dependencies: - dependency-name: org.slf4j:slf4j-api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump com.azure:azure-security-keyvault-secrets (#235) Bumps [com.azure:azure-security-keyvault-secrets](https://github.com/Azure/azure-sdk-for-java) from 4.5.4 to 4.6.0. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-security-keyvault-keys_4.5.4...azure-cosmos_4.6.0) --- updated-dependencies: - dependency-name: com.azure:azure-security-keyvault-secrets dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump com.diffplug.spotless from 6.15.0 to 6.18.0 (#236) Bumps com.diffplug.spotless from 6.15.0 to 6.18.0. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump com.github.johnrengelman.shadow from 8.0.0 to 8.1.1 (#237) * chore(deps): bump io.freefair.lombok from 6.6.2 to 8.0.1 (#238) * chore(deps): bump org.flywaydb:flyway-core from 9.15.2 to 9.16.3 (#242) * chore(deps): bump com.google.code.gson:gson from 2.10 to 2.10.1 (#243) Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.10 to 2.10.1. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.10...gson-parent-2.10.1) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor: update GitHub output command to current version (#233) * refactor GitHub output command to current version * Remove curly braces from output statement * fix: only run trivy when docker images were actually built (#240) * fix: run trivy only if image exists * update checks * refactor: Extract the setup-java action into a re-usable action (#246) * Extract the checkout and setup-java action into a re-usable action * Commit actions. * fix action * remove checkout extraction * feat(BusinessPartnerValidation): adds logging if it's enabled on contract agreement validation (#245) * feat(BusinessPartnerValidation): adds logging if it's enabled on contract agreement validation * feat(BusinessPartnerValidation): adds logging on tests * feat(BusinessPartnerValidation): enabled by default on charts config * pr remarks * release-fix: use correct value * Prepare release 0.3.3 --------- Signed-off-by: dependabot[bot] Co-authored-by: Tuncay Tunc Co-authored-by: Enrico Risa Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) Co-authored-by: Sebastian Bezold Co-authored-by: Paul Latzelsperger Co-authored-by: GitHub actions Co-authored-by: Stephan Bauer Co-authored-by: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Co-authored-by: Sigi <47592287+Siegfriedk@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tuncay Tunc (ZF Friedrichshafen AG) <100704677+tuncaytunc-zf@users.noreply.github.com> Co-authored-by: Sascha Isele (ZF Friedrichshafen AG) <127207440+saschaisele-zf@users.noreply.github.com> Co-authored-by: Garrett Smith <42892027+gcs14@users.noreply.github.com> --- .github/PULL_REQUEST_TEMPLATE.md | 13 + .../actions/publish-docker-image/action.yml | 109 ++ .../actions/run-deployment-test/action.yml | 97 ++ .../actions/setup-java/action.yml | 24 +- .github/dependabot.yml | 16 +- .github/workflows/build.yaml | 203 +-- .github/workflows/business-tests.yaml | 24 +- .github/workflows/deploy-test-secrets | 51 + .github/workflows/deployment-test.yaml | 66 + .github/workflows/draft-new-release.yaml | 13 +- .github/workflows/helm-chart-release.yaml | 5 +- .github/workflows/helm-lint.yaml | 9 +- .github/workflows/kics.yml | 4 +- .github/workflows/publish-docker.yaml | 83 ++ .github/workflows/publish-new-release.yml | 52 +- .github/workflows/trivy.yml | 46 +- .github/workflows/veracode.yaml | 66 +- .github/workflows/verify.yaml | 97 +- .markdownlint.yaml | 1 + CHANGELOG.md | 348 ++--- NOTICE.md | 1 + README.md | 30 +- build.gradle.kts | 15 +- charts/edc-controlplane/Chart.yaml | 35 - charts/edc-controlplane/LICENSE | 202 --- charts/edc-controlplane/README.md | 106 -- charts/edc-controlplane/templates/NOTES.txt | 74 - .../edc-controlplane/templates/_helpers.tpl | 72 - .../edc-controlplane/templates/configmap.yaml | 49 - .../templates/deployment.yaml | 154 -- charts/edc-controlplane/templates/hpa.yaml | 52 - .../templates/imagepullsecret.yaml | 35 - .../edc-controlplane/templates/ingress.yaml | 100 -- .../edc-controlplane/templates/service.yaml | 59 - .../templates/serviceaccount.yaml | 36 - charts/edc-controlplane/values.yaml | 379 ----- charts/edc-dataplane/Chart.yaml | 35 - charts/edc-dataplane/LICENSE | 202 --- charts/edc-dataplane/README.md | 90 -- charts/edc-dataplane/README.md.gotmpl | 26 - charts/edc-dataplane/templates/NOTES.txt | 64 - charts/edc-dataplane/templates/configmap.yaml | 45 - .../edc-dataplane/templates/deployment.yaml | 142 -- charts/edc-dataplane/templates/hpa.yaml | 52 - .../templates/imagepullsecret.yaml | 35 - charts/edc-dataplane/templates/service.yaml | 51 - .../templates/serviceaccount.yaml | 36 - charts/edc-dataplane/values.yaml | 331 ----- .../.helmignore | 6 - charts/tractusx-connector-memory/Chart.yaml | 45 + charts/tractusx-connector-memory/README.md | 147 ++ .../README.md.gotmpl | 2 +- charts/tractusx-connector-memory/example.yaml | 65 + .../templates/NOTES.txt | 22 + .../templates/_helpers.tpl | 157 ++ .../templates/configmap-runtime.yaml | 33 + .../templates/deployment-runtime.yaml | 308 ++++ .../templates/hpa-runtime.yaml | 29 + .../templates/ingress-runtime.yaml} | 45 +- .../templates/service-runtime.yaml | 59 + .../templates/serviceaccount.yaml | 16 + .../templates/tests/test-readiness.yaml | 15 + charts/tractusx-connector-memory/values.yaml | 316 ++++ charts/tractusx-connector/Chart.yaml | 4 +- charts/tractusx-connector/README.md | 31 +- .../tractusx-connector/templates/_helpers.tpl | 12 +- .../templates/deployment-controlplane.yaml | 63 +- .../templates/deployment-dataplane.yaml | 60 +- .../templates/service-controlplane.yaml | 16 +- .../templates/service-dataplane.yaml | 4 + charts/tractusx-connector/values.yaml | 27 +- docs/README.md | 12 +- .../2023-02-09-release-process/README.md | 18 +- .../2023-02-27_testing/README.md | 2 +- .../2023-04-03_renaming_branches/README.md | 61 + .../2023-04-11_refactor_helmcharts/README.md | 112 ++ docs/migration/Version_0.3.1_0.3.2.md | 2 +- edc-controlplane/build.gradle.kts | 3 +- .../build.gradle.kts | 2 +- .../notice.md | 28 + .../src/main/docker/Dockerfile | 2 +- .../build.gradle.kts | 2 +- .../notice.md | 28 + .../src/main/docker/Dockerfile | 4 +- .../build.gradle.kts | 5 +- .../edc-controlplane-postgresql/notice.md | 28 + .../src/main/docker/Dockerfile | 4 +- .../README.md | 109 +- .../build.gradle.kts | 13 +- edc-controlplane/edc-runtime-memory/notice.md | 28 + .../src/main/docker/Dockerfile | 24 +- .../edc/vault/memory/InMemoryVault.java | 53 + .../vault/memory/VaultMemoryExtension.java | 54 + ...rg.eclipse.edc.spi.system.ServiceExtension | 15 +- .../edc/vault/memory/InMemoryVaultTest.java | 56 + .../memory/VaultMemoryExtensionTest.java | 52 + .../build.gradle.kts | 4 +- .../edc-dataplane-azure-vault/notice.md | 28 + .../src/main/docker/Dockerfile | 4 +- .../edc-dataplane-base/build.gradle.kts | 22 +- .../build.gradle.kts | 2 +- .../edc-dataplane-hashicorp-vault/notice.md | 28 + .../src/main/docker/Dockerfile | 4 +- .../build.gradle.kts | 2 +- .../BusinessPartnerValidationExtension.java | 126 +- .../AbstractBusinessPartnerValidation.java | 231 +-- .../BusinessPartnerDutyFunction.java | 20 +- .../BusinessPartnerPermissionFunction.java | 22 +- .../BusinessPartnerProhibitionFunction.java | 22 +- ...usinessPartnerValidationExtensionTest.java | 23 + ...AbstractBusinessPartnerValidationTest.java | 239 +-- .../control-plane-adapter/README.md | 10 +- .../algorithms/aes/AesAlgorithm.java | 132 +- .../aes/AesInitializationVectorIterator.java | 63 +- .../algorithms/aes/ByteCounter.java | 102 +- .../data/CryptoDataFactoryImpl.java | 131 +- .../AesDataEncrypterConfiguration.java | 28 +- .../encrypter/AesDataEncrypterImpl.java | 137 +- .../encrypter/DataEncrypterFactory.java | 74 +- .../encryption/key/CryptoKeyFactoryImpl.java | 22 +- .../encryption/provider/AesKeyProvider.java | 73 +- .../provider/CachingKeyProvider.java | 105 +- .../algorithms/aes/AesAlgorithmTest.java | 102 +- .../AesInitializationVectorIteratorTest.java | 80 +- .../DataEncrypterAesComponentTest.java | 125 +- .../hashicorp-vault/build.gradle.kts | 4 +- .../postgresql-migration/build.gradle.kts | 2 +- edc-tests/cucumber/build.gradle.kts | 10 +- .../helm/supporting-infrastructure/Chart.yaml | 6 - .../supporting-infrastructure/values.yaml | 13 - .../edc/tests/BackendDataService.java | 35 + .../edc/tests/BackendServiceBackendAPI.java | 270 ---- .../edc/tests/BackendServiceSteps.java | 11 +- .../eclipse/tractusx/edc/tests/Connector.java | 83 +- .../tractusx/edc/tests/ConnectorFactory.java | 27 +- .../eclipse/tractusx/edc/tests/Constants.java | 25 +- .../edc/tests/ControlPlaneAdapterSteps.java | 82 +- .../tractusx/edc/tests/DataManagementAPI.java | 1309 +++++++++-------- .../tractusx/edc/tests/Environment.java | 203 ++- .../edc/tests/HttpProxyTransferSteps.java | 166 ++- .../tractusx/edc/tests/NegotiationSteps.java | 89 +- .../tractusx/edc/tests/PolicyStepDefs.java | 65 +- .../edc/tests/S3FileTransferStepsDefs.java | 227 ++- .../tractusx/edc/tests/data/Asset.java | 26 +- .../data/BusinessPartnerNumberConstraint.java | 14 +- .../edc/tests/data/ContractDefinition.java | 41 +- .../edc/tests/data/ContractNegotiation.java | 29 +- .../edc/tests/data/ContractOffer.java | 28 +- .../data/HttpProxySourceDataAddress.java | 60 +- .../tractusx/edc/tests/data/Negotiation.java | 57 +- .../tractusx/edc/tests/data/OrConstraint.java | 15 +- .../edc/tests/data/PayMeConstraint.java | 14 +- .../tractusx/edc/tests/data/Permission.java | 29 +- .../tractusx/edc/tests/data/Policy.java | 22 +- .../edc/tests/data/S3DataAddress.java | 28 +- .../tractusx/edc/tests/data/Transfer.java | 46 +- .../edc/tests/data/TransferProcess.java | 21 +- .../edc/tests/util/DatabaseCleaner.java | 45 +- .../tractusx/edc/tests/util/S3Client.java | 166 +-- .../features/HttpProxyDataTransfer.feature | 18 - .../main/resources/helm/omejdn}/.helmignore | 6 - .../src/main/resources/helm/omejdn/Chart.yaml | 25 + .../src/main/resources/helm/omejdn/README.md | 21 + .../helm/omejdn}/templates/_helpers.tpl | 30 +- .../helm/omejdn/templates/configmap.yaml | 73 + .../helm/omejdn/templates/deployment.yaml | 149 ++ .../resources/helm/omejdn/templates/hpa.yaml | 28 + .../omejdn/templates/imagepullsecret.yaml | 13 + .../helm/omejdn/templates/service.yaml | 15 + .../helm/omejdn/templates/serviceaccount.yaml | 12 + .../main/resources/helm/omejdn/values.yaml | 91 ++ .../helm/test-infrastructure/.gitignore | 4 + .../helm/test-infrastructure/.helmignore | 24 + .../helm/test-infrastructure/Chart.yaml | 54 + .../helm/test-infrastructure/README.md | 54 + .../helm/test-infrastructure/values.yaml | 185 +++ edc-tests/e2e-tests/build.gradle.kts | 8 +- .../edc/lifecycle/MultiRuntimeTest.java | 86 +- .../tractusx/edc/lifecycle/Participant.java | 169 ++- .../lifecycle/TestRuntimeConfiguration.java | 45 +- .../provider/ProviderEdcController.java | 2 + .../provider/ProviderServicesExtension.java | 2 + .../edc/policy/PolicyHelperFunctions.java | 13 + .../tractusx/edc/tests/CatalogTest.java | 25 +- .../tests/HttpConsumerPullWithProxyTest.java | 121 ++ edc-tests/runtime/build.gradle.kts | 16 +- .../ConsumerEdrHandlerController.java | 61 + .../lifecycle/ConsumerServicesExtension.java | 30 + ...rg.eclipse.edc.spi.system.ServiceExtension | 15 + gradle.properties | 2 +- pr_etiquette.md | 13 +- settings.gradle.kts | 15 +- 192 files changed, 6835 insertions(+), 5853 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/actions/publish-docker-image/action.yml create mode 100644 .github/actions/run-deployment-test/action.yml rename charts/edc-dataplane/templates/configmap-env.yaml => .github/actions/setup-java/action.yml (63%) create mode 100644 .github/workflows/deploy-test-secrets create mode 100644 .github/workflows/deployment-test.yaml create mode 100644 .github/workflows/publish-docker.yaml delete mode 100644 charts/edc-controlplane/Chart.yaml delete mode 100644 charts/edc-controlplane/LICENSE delete mode 100644 charts/edc-controlplane/README.md delete mode 100644 charts/edc-controlplane/templates/NOTES.txt delete mode 100644 charts/edc-controlplane/templates/_helpers.tpl delete mode 100644 charts/edc-controlplane/templates/configmap.yaml delete mode 100644 charts/edc-controlplane/templates/deployment.yaml delete mode 100644 charts/edc-controlplane/templates/hpa.yaml delete mode 100644 charts/edc-controlplane/templates/imagepullsecret.yaml delete mode 100644 charts/edc-controlplane/templates/ingress.yaml delete mode 100644 charts/edc-controlplane/templates/service.yaml delete mode 100644 charts/edc-controlplane/templates/serviceaccount.yaml delete mode 100644 charts/edc-controlplane/values.yaml delete mode 100644 charts/edc-dataplane/Chart.yaml delete mode 100644 charts/edc-dataplane/LICENSE delete mode 100644 charts/edc-dataplane/README.md delete mode 100644 charts/edc-dataplane/README.md.gotmpl delete mode 100644 charts/edc-dataplane/templates/NOTES.txt delete mode 100644 charts/edc-dataplane/templates/configmap.yaml delete mode 100644 charts/edc-dataplane/templates/deployment.yaml delete mode 100644 charts/edc-dataplane/templates/hpa.yaml delete mode 100644 charts/edc-dataplane/templates/imagepullsecret.yaml delete mode 100644 charts/edc-dataplane/templates/service.yaml delete mode 100644 charts/edc-dataplane/templates/serviceaccount.yaml delete mode 100644 charts/edc-dataplane/values.yaml rename charts/{edc-controlplane => tractusx-connector-memory}/.helmignore (82%) create mode 100644 charts/tractusx-connector-memory/Chart.yaml create mode 100644 charts/tractusx-connector-memory/README.md rename charts/{edc-controlplane => tractusx-connector-memory}/README.md.gotmpl (86%) create mode 100644 charts/tractusx-connector-memory/example.yaml create mode 100644 charts/tractusx-connector-memory/templates/NOTES.txt create mode 100644 charts/tractusx-connector-memory/templates/_helpers.tpl create mode 100644 charts/tractusx-connector-memory/templates/configmap-runtime.yaml create mode 100644 charts/tractusx-connector-memory/templates/deployment-runtime.yaml create mode 100644 charts/tractusx-connector-memory/templates/hpa-runtime.yaml rename charts/{edc-dataplane/templates/ingress.yaml => tractusx-connector-memory/templates/ingress-runtime.yaml} (58%) create mode 100644 charts/tractusx-connector-memory/templates/service-runtime.yaml create mode 100644 charts/tractusx-connector-memory/templates/serviceaccount.yaml create mode 100644 charts/tractusx-connector-memory/templates/tests/test-readiness.yaml create mode 100644 charts/tractusx-connector-memory/values.yaml create mode 100644 docs/development/decision-records/2023-04-03_renaming_branches/README.md create mode 100644 docs/development/decision-records/2023-04-11_refactor_helmcharts/README.md create mode 100644 edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md create mode 100644 edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md create mode 100644 edc-controlplane/edc-controlplane-postgresql/notice.md rename edc-controlplane/{edc-controlplane-memory => edc-runtime-memory}/README.md (54%) rename edc-controlplane/{edc-controlplane-memory => edc-runtime-memory}/build.gradle.kts (68%) create mode 100644 edc-controlplane/edc-runtime-memory/notice.md rename edc-controlplane/{edc-controlplane-memory => edc-runtime-memory}/src/main/docker/Dockerfile (59%) create mode 100644 edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVault.java create mode 100644 edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java rename charts/edc-controlplane/templates/configmap-env.yaml => edc-controlplane/edc-runtime-memory/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension (62%) create mode 100644 edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVaultTest.java create mode 100644 edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java create mode 100644 edc-dataplane/edc-dataplane-azure-vault/notice.md create mode 100644 edc-dataplane/edc-dataplane-hashicorp-vault/notice.md create mode 100644 edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendDataService.java delete mode 100644 edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java rename {charts/edc-dataplane => edc-tests/deployment/src/main/resources/helm/omejdn}/.helmignore (82%) create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/README.md rename {charts/edc-dataplane => edc-tests/deployment/src/main/resources/helm/omejdn}/templates/_helpers.tpl (66%) create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md create mode 100644 edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml create mode 100644 edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderEdcController.java create mode 100644 edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderServicesExtension.java create mode 100644 edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java create mode 100644 edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java create mode 100644 edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java create mode 100644 edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..fe4467a54 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +## WHAT + +_Briefly describe what your PR changes, which features it adds/modifies._ + +## WHY + +_Briefly state why the change was necessary._ + +## FURTHER NOTES + +_List other areas of code that have changed but are not necessarily linked to the main feature. This could be method signature changes, package declarations, bugs that were encountered and were fixed inline, etc._ + +Closes # <-- _insert Issue number if one exists_ diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml new file mode 100644 index 000000000..2f8a8c522 --- /dev/null +++ b/.github/actions/publish-docker-image/action.yml @@ -0,0 +1,109 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +name: "Publish Docker Image" +description: "Build and publish a Docker Image to DockerHub" +inputs: + rootDir: + required: true + description: "The directory where the notice.md file and the src/main/docker directory are located" + namespace: + required: false + default: "tractusx" + description: "The Docker image namespace" + imagename: + required: true + description: "the name of the image" + docker_user: + required: false + description: "DockerHub user name. No push is done if omitted" + docker_token: + required: false + description: "DockerHub Token. No push is done if omitted" +runs: + using: "composite" + steps: + - uses: actions/checkout@v3.3.0 + + ##################### + # Login to DockerHub + ##################### + - name: DockerHub login + uses: docker/login-action@v2 + with: + username: ${{ inputs.docker_user }} + password: ${{ inputs.docker_token }} + + ##################### + # Build JAR file + ##################### + - uses: ./.github/actions/setup-java + - name: Build Controlplane + shell: bash + run: |- + ./gradlew -p ${{ inputs.rootDir }} shadowJar + + ############################### + # Set metadata of docker image + ############################### + # Create SemVer or ref tags dependent of trigger event + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ${{ inputs.namespace }}/${{ inputs.imagename }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{raw}} + type=match,pattern=\d.\d.\d + type=raw,value=latest,enable={{is_default_branch}} + type=sha + + ############################### + # Build and push the image + ############################### + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + file: ${{ inputs.rootDir }}/src/main/docker/Dockerfile + build-args: | + JAR=${{ inputs.rootDir }}/build/libs/${{ inputs.imagename }}.jar + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + ############################### + # Update the description + # https://github.com/peter-evans/dockerhub-description + ############################### + - name: Update Docker Hub description + uses: peter-evans/dockerhub-description@v3 + with: + readme-filepath: ${{ inputs.rootDir }}/notice.md + username: ${{ inputs.docker_user }} + password: ${{ inputs.docker_token }} + repository: ${{ inputs.namespace }}/${{ inputs.imagename }} \ No newline at end of file diff --git a/.github/actions/run-deployment-test/action.yml b/.github/actions/run-deployment-test/action.yml new file mode 100644 index 000000000..9f4b40d58 --- /dev/null +++ b/.github/actions/run-deployment-test/action.yml @@ -0,0 +1,97 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +name: "Run Deployment Test" +description: "Build and publish a Docker Image to DockerHub" +inputs: + imagename: + required: true + description: "name of the docker image, e.g. edc-runtime-memory" + + image_tag: + required: false + default: "latest" + description: "docker image tag, defaults to 'latest'" + + helm_command: + required: true + description: "command which is executed to install the chart. must also include verification commands, such as 'helm test'" + + rootDir: + required: true + description: "The directory that contains the docker file, e.g. edc-controlplane/edc-runtime-memory" + +runs: + using: "composite" + steps: + - uses: actions/checkout@v3.3.0 + + - name: Cache ContainerD Image Layers + uses: actions/cache@v3 + with: + path: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs + key: ${{ runner.os }}-io.containerd.snapshotter.v1.overlayfs + + - uses: ./.github/actions/setup-java + + - name: Build docker images + shell: bash + run: |- + ./gradlew -p ${{ inputs.rootDir }} dockerize + + - name: Setup Helm + uses: azure/setup-helm@v3.5 + with: + version: v3.8.1 + + - name: Setup Kubectl + uses: azure/setup-kubectl@v3.2 + + - name: Create k8s Kind Cluster + uses: helm/kind-action@v1.5.0 + + - name: Load images into KinD + shell: bash + run: | + kind get clusters | xargs -n1 kind load docker-image ${{ inputs.imagename }}:${{ inputs.image_tag }} --name + + ################################################### + # Install the test infrastructure + ################################################### + - name: Install Infrastructure + shell: bash + run: |- + helm install infra edc-tests/deployment/src/main/resources/helm/test-infrastructure \ + --wait-for-jobs --timeout=30s --dependency-update + + - name: Install Runtime + shell: bash + run: ${{ inputs.helm_command }} + + + ################# + ### Tear Down ### + ################# + - name: Destroy the kind cluster + if: always() + shell: bash + run: >- + kind get clusters | xargs -n1 kind delete cluster --name \ No newline at end of file diff --git a/charts/edc-dataplane/templates/configmap-env.yaml b/.github/actions/setup-java/action.yml similarity index 63% rename from charts/edc-dataplane/templates/configmap-env.yaml rename to .github/actions/setup-java/action.yml index 0e021734a..ed03fafb3 100644 --- a/charts/edc-dataplane/templates/configmap-env.yaml +++ b/.github/actions/setup-java/action.yml @@ -1,8 +1,6 @@ # -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# Copyright (c) 2023 Contributors to the Eclipse Foundation # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -21,12 +19,14 @@ # --- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "edc-dataplane.fullname" . }}-env - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -data: - {{- toYaml .Values.env | nindent 2 }} +name: "Setup JDK 17" +description: "Setup JDK 17" +runs: + using: "composite" + steps: + - name: Setup JDK 17 + uses: actions/setup-java@v3.11.0 + with: + java-version: '17' + distribution: 'temurin' + cache: 'gradle' \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 16f8582cb..b59a06386 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,8 +3,8 @@ version: 2 updates: # Maven - - package-ecosystem: "maven" - target-branch: develop + package-ecosystem: "gradle" + target-branch: main directory: / labels: - "dependabot" @@ -15,7 +15,7 @@ updates: # Github Actions - package-ecosystem: "github-actions" - target-branch: develop + target-branch: main directory: / labels: - "dependabot" @@ -26,7 +26,7 @@ updates: # Docker - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/ labels: - "dependabot" @@ -35,7 +35,7 @@ updates: interval: "daily" - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-controlplane/edc-controlplane-postgresql/src/main/docker/ labels: - "dependabot" @@ -44,7 +44,7 @@ updates: interval: "daily" - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-controlplane/edc-controlplane-memory/src/main/docker/ labels: - "dependabot" @@ -53,7 +53,7 @@ updates: interval: "daily" - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-dataplane/edc-dataplane-azure-vault/src/main/docker/ labels: - "dependabot" @@ -62,7 +62,7 @@ updates: interval: "daily" - package-ecosystem: "docker" - target-branch: develop + target-branch: main directory: ./edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/ labels: - "dependabot" diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b8ab83bba..0713b0857 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -27,7 +27,7 @@ on: push: branches: - main - - develop + - releases tags: - '[0-9]+.[0-9]+.[0-9]+' release: @@ -44,7 +44,8 @@ on: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # cancel only running jobs on pull requests + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: @@ -54,34 +55,26 @@ jobs: SONAR_TOKEN: ${{ steps.secret-presence.outputs.SONAR_TOKEN }} GPG_PRIVATE_KEY: ${{ steps.secret-presence.outputs.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ steps.secret-presence.outputs.GPG_PASSPHRASE }} + DOCKER_HUB_TOKEN: ${{ steps.secret-presence.outputs.DOCKER_HUB_TOKEN }} steps: - - - name: Check whether secrets exist + - name: Check whether secrets exist id: secret-presence run: | - [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "::set-output name=SONAR_TOKEN::true" - [ ! -z "${{ secrets.GPG_PRIVATE_KEY }}" ] && echo "::set-output name=GPG_PRIVATE_KEY::true" - [ ! -z "${{ secrets.GPG_PASSPHRASE }}" ] && echo "::set-output name=GPG_PASSPHRASE::true" + [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "SONAR_TOKEN=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.GPG_PRIVATE_KEY }}" ] && echo "GPG_PRIVATE_KEY=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.GPG_PASSPHRASE }}" ] && echo "GPG_PASSPHRASE=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.DOCKER_HUB_TOKEN }}" ] && echo "DOCKER_HUB_TOKEN=true" >> $GITHUB_OUTPUT exit 0 build-extensions: runs-on: ubuntu-latest - needs: [ secret-presence] + needs: [ secret-presence ] steps: # Set-Up - - - name: Checkout - uses: actions/checkout@v3.3.0 - - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/setup-java # Build - - - name: Build Extensions + - name: Build Extensions run: |- ./gradlew -p edc-extensions build env: @@ -89,167 +82,73 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} build-controlplane: + name: "Create Docker Images for the ControlPlane" runs-on: ubuntu-latest - permissions: - contents: read - packages: write - needs: [ secret-presence] + needs: [ secret-presence ] + if: | + needs.secret-presence.outputs.DOCKER_HUB_TOKEN strategy: fail-fast: false matrix: name: - - edc-controlplane-memory + - edc-runtime-memory - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault + permissions: + contents: write steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3.3.0 - - - name: Login to GitHub Container Registry - if: | - github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' - # Build - - - name: Build Controlplane - run: |- - ./gradlew -p edc-controlplane/${{ matrix.name }} shadowJar - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: edc-controlplane Docker Metadata - id: edc_controlplane_meta - uses: docker/metadata-action@v4 + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/publish-docker-image with: - images: | - ghcr.io/${{ github.repository }}/${{ matrix.name }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build Docker Image - uses: docker/build-push-action@v4 - with: - context: . - file: edc-controlplane/${{ matrix.name }}/src/main/docker/Dockerfile - build-args: | - JAR=edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - push: | - ${{ (github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_controlplane_meta.outputs.tags }} - labels: ${{ steps.edc_controlplane_meta.outputs.labels }} + rootDir: edc-controlplane/${{ matrix.name }} + imagename: ${{ matrix.name }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} build-dataplane: + name: "Create Docker Images for the DataPlane" runs-on: ubuntu-latest - permissions: - contents: read - packages: write - needs: [ secret-presence] + needs: [ secret-presence ] + if: | + needs.secret-presence.outputs.DOCKER_HUB_TOKEN strategy: fail-fast: false matrix: name: - edc-dataplane-azure-vault - edc-dataplane-hashicorp-vault + permissions: + contents: write steps: - # Set-Up - - - name: Checkout - uses: actions/checkout@v3.3.0 - - - name: Login to GitHub Container Registry - if: | - github.event_name != 'pull_request' - uses: docker/login-action@v2 + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/publish-docker-image with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' - # Build - - - name: Build Dataplane - run: |- - ./gradlew -p edc-dataplane/${{ matrix.name }} shadowJar - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: edc-dataplane Docker Metadata - id: edc_dataplane_meta - uses: docker/metadata-action@v4 - with: - images: | - ghcr.io/${{ github.repository }}/${{ matrix.name }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{raw}} - type=match,pattern=\d.\d.\d - type=sha - - - name: Build Docker Image - uses: docker/build-push-action@v4 - with: - context: . - file: edc-dataplane/${{ matrix.name }}/src/main/docker/Dockerfile - build-args: | - JAR=edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - push: | - ${{ (github.event_name != 'pull_request' && 'true') || 'false' }} - tags: ${{ steps.edc_dataplane_meta.outputs.tags }} - labels: ${{ steps.edc_dataplane_meta.outputs.labels }} + rootDir: edc-dataplane/${{ matrix.name }} + imagename: ${{ matrix.name }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} publish-to-github-packages: runs-on: ubuntu-latest permissions: contents: read packages: write - needs: [secret-presence, build-controlplane, build-dataplane, build-extensions] + needs: [ secret-presence, build-extensions ] - # do not run on PR branches, do not run on main + # do not run on PR branches, do not run on releases if: | - needs.secret-presence.outputs.GPG_PASSPHRASE && needs.secret-presence.outputs.GPG_PRIVATE_KEY && github.event_name != 'pull_request' && github.ref != 'refs/heads/main' + needs.secret-presence.outputs.GPG_PASSPHRASE && needs.secret-presence.outputs.GPG_PRIVATE_KEY && github.event_name != 'pull_request' && github.ref != 'refs/heads/releases' steps: # Set-Up - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Import GPG Key - uses: crazy-max/ghaction-import-gpg@v1 - env: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + uses: crazy-max/ghaction-import-gpg@v5 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} # publish snapshots - name: Publish snapshot versions @@ -257,7 +156,7 @@ jobs: echo "Publishing Version $(grep -e "version" gradle.properties | cut -f2 -d"=") to Github Packages" ./gradlew publishAllPublicationsToGitHubPackagesRepository env: - #REPO: ${{ github.repository }} - REPO: "catenax-ng/product-edc" - GITHUB_PACKAGE_USERNAME: ${{ secrets.TEMP_GHPKG_USER }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.TEMP_GHPKG_PASSWORD }} + REPO: ${{ github.repository }} + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index 6a7cd2cbf..cbf0f3767 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -29,13 +29,14 @@ on: - 'docs/**' - '**/*.md' branches: - - develop + - releases - release/** - main workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + # cancel only running jobs on pull requests + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: @@ -49,15 +50,9 @@ jobs: ### Set-Up ### ############## - - name: Checkout uses: actions/checkout@v3.3.0 - - name: Set-Up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + uses: ./.github/actions/setup-java - name: Cache ContainerD Image Layers uses: actions/cache@v3 @@ -128,14 +123,14 @@ jobs: run: |- # Define endpoints echo "SOKRATES_DATA_MANAGEMENT_API_AUTH_KEY=password" | tee -a ${GITHUB_ENV} - echo "SOKRATES_DATA_MANAGEMENT_URL=http://sokrates-controlplane:8081/data" | tee -a ${GITHUB_ENV} + echo "SOKRATES_DATA_MANAGEMENT_URL=http://sokrates-controlplane:8081/management" | tee -a ${GITHUB_ENV} echo "SOKRATES_IDS_URL=http://sokrates-controlplane:8084/api/v1/ids" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATA_PLANE_URL=http://sokrates-dataplane:8081/api/public/" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_URL=jdbc:postgresql://plato-postgresql:5432/edc" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_USER=user" | tee -a ${GITHUB_ENV} echo "SOKRATES_DATABASE_PASSWORD=password" | tee -a ${GITHUB_ENV} echo "PLATO_DATA_MANAGEMENT_API_AUTH_KEY=password" | tee -a ${GITHUB_ENV} - echo "PLATO_DATA_MANAGEMENT_URL=http://plato-controlplane:8081/data" | tee -a ${GITHUB_ENV} + echo "PLATO_DATA_MANAGEMENT_URL=http://plato-controlplane:8081/management" | tee -a ${GITHUB_ENV} echo "PLATO_IDS_URL=http://plato-controlplane:8084/api/v1/ids" | tee -a ${GITHUB_ENV} echo "PLATO_DATA_PLANE_URL=http://plato-dataplane:8081/api/public/" | tee -a ${GITHUB_ENV} echo "PLATO_DATABASE_URL=jdbc:postgresql://plato-postgresql:5432/edc" | tee -a ${GITHUB_ENV} @@ -165,7 +160,6 @@ jobs: sleep 5s # Wait for supporting infrastructure to become ready (control-/data-plane, backend service) - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=backend --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=backend --tail 500 && exit 1 ) kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=idsdaps --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=idsdaps --tail 500 && exit 1 ) kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=vault --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=vault --tail 500 && exit 1 ) kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=sokrates-postgresql --timeout=120s || ( kubectl logs -l app.kubernetes.io/name=sokrates-postgresql --tail 500 && exit 1 ) @@ -176,7 +170,7 @@ jobs: helm install plato charts/tractusx-connector \ --set fullnameOverride=plato \ --set controlplane.service.type=NodePort \ - --set controlplane.endpoints.data.authKey=password \ + --set controlplane.endpoints.management.authKey=password \ --set controlplane.image.tag=business-test \ --set controlplane.image.pullPolicy=Never \ --set controlplane.image.repository=docker.io/library/edc-controlplane-postgresql-hashicorp-vault \ @@ -185,6 +179,7 @@ jobs: --set dataplane.image.repository=docker.io/library/edc-dataplane-hashicorp-vault \ --set controlplane.debug.enabled=true \ --set controlplane.suspendOnStart=false \ + --set controlplane.businesspartnervalidation.log.agreement.validation=true \ --set postgresql.enabled=true \ --set postgresql.username=user \ --set postgresql.password=password \ @@ -209,7 +204,7 @@ jobs: helm install sokrates charts/tractusx-connector \ --set fullnameOverride=sokrates \ --set controlplane.service.type=NodePort \ - --set controlplane.endpoints.data.authKey=password \ + --set controlplane.endpoints.management.authKey=password \ --set controlplane.image.tag=business-test \ --set controlplane.image.pullPolicy=Never \ --set controlplane.image.repository=docker.io/library/edc-controlplane-postgresql-hashicorp-vault \ @@ -218,6 +213,7 @@ jobs: --set dataplane.image.repository=docker.io/library/edc-dataplane-hashicorp-vault \ --set controlplane.debug.enabled=true \ --set controlplane.suspendOnStart=false \ + --set controlplane.businesspartnervalidation.log.agreement.validation=true \ --set postgresql.enabled=true \ --set postgresql.username=user \ --set postgresql.password=password \ diff --git a/.github/workflows/deploy-test-secrets b/.github/workflows/deploy-test-secrets new file mode 100644 index 000000000..28596b459 --- /dev/null +++ b/.github/workflows/deploy-test-secrets @@ -0,0 +1,51 @@ +daps-key:-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM +wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ +FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 +8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ +ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE +sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc +RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z +d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm +hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm +cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh +FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F +MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e +uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb +ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 +z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 +h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ +vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB +8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 +hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 +dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 +Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 +IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT +3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr +0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 +u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B +AjWFbUiBCFOo+gpAFcQGrkOQHA== +-----END PRIVATE KEY-----;daps-crt:-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL +BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl +cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 +bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ +TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE +BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK +DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD +DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw +78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa +llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV +grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 +PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 +ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT +MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH +LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd +iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E +28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 +S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r +uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB +UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml new file mode 100644 index 000000000..8e75ae31e --- /dev/null +++ b/.github/workflows/deployment-test.yaml @@ -0,0 +1,66 @@ +# +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +name: "Deployment Tests" + +on: + push: + branches: + - main + - develop + tags: + - '[0-9]+.[0-9]+.[0-9]+' + release: + types: + - published + pull_request: + paths-ignore: + - 'docs/**' + - '**/*.md' + branches: + - '*' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + deployment-test-memory: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/run-deployment-test + name: "Run deployment test using KinD and Helm" + with: + imagename: edc-runtime-memory + rootDir: edc-controlplane/edc-runtime-memory + helm_command: |- + helm install tx-inmem charts/tractusx-connector-memory \ + -f charts/tractusx-connector-memory/example.yaml \ + --set vault.secrets="$(cat ./.github/workflows/deploy-test-secrets)" \ + --wait-for-jobs --timeout=120s + + # wait for the pod to become ready + kubectl rollout status deployment tx-inmem + + # execute the helm test + helm test tx-inmem diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index acb4412d9..98e3c956a 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -33,12 +33,7 @@ jobs: git config user.name "GitHub actions" git config user.email noreply@github.com - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + uses: ./.github/actions/setup-java - name: Bump version in gradle.properties run: |- @@ -49,7 +44,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Bump version in /charts - uses: mikefarah/yq@v4.31.2 + uses: mikefarah/yq@v4.33.3 with: cmd: |- find charts -name Chart.yaml | xargs -n1 yq -i '.appVersion = "${{ github.event.inputs.version }}" | .version = "${{ github.event.inputs.version }}"' @@ -68,7 +63,7 @@ jobs: git add CHANGELOG.md gradle.properties $(find charts -name Chart.yaml) $(find charts -name README.md) git commit --message "Prepare release ${{ github.event.inputs.version }}" - echo "::set-output name=commit::$(git rev-parse HEAD)" + echo "commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - name: Push new branch run: git push origin release/${{ github.event.inputs.version }} @@ -79,7 +74,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: head: release/${{ github.event.inputs.version }} - base: main + base: releases title: Release version ${{ github.event.inputs.version }} reviewers: ${{ github.actor }} body: |- diff --git a/.github/workflows/helm-chart-release.yaml b/.github/workflows/helm-chart-release.yaml index 819f4f0ec..f19c841b9 100644 --- a/.github/workflows/helm-chart-release.yaml +++ b/.github/workflows/helm-chart-release.yaml @@ -23,7 +23,7 @@ on: paths: - 'charts/**' branches: - - main + - releases workflow_dispatch: jobs: @@ -38,8 +38,7 @@ jobs: steps: # fetch-depth: 0 is required to determine differences in chart(s) - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 with: fetch-depth: 0 diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 624607533..0b5a70f1f 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -5,7 +5,7 @@ on: push: branches: - main - - develop + - releases tags: - '[0-9]+.[0-9]+.[0-9]+' paths-ignore: @@ -26,7 +26,6 @@ jobs: ### Set-Up ### ############## - - name: Checkout uses: actions/checkout@v3.3.0 with: fetch-depth: 0 @@ -42,7 +41,7 @@ jobs: python-version: 3.7 - name: chart-testing (setup) - uses: helm/chart-testing-action@v2.3.1 + uses: helm/chart-testing-action@v2.4.0 ##################### ### Chart Testing ### ##################### @@ -50,9 +49,9 @@ jobs: name: chart-testing (list-changed) id: list-changed run: | - changed=$(ct list-changed --config ct.yaml --target-branch develop) + changed=$(ct list-changed --config ct.yaml --target-branch main) if [[ -n "$changed" ]]; then - echo "::set-output name=changed::true" + echo "changed=true" >> $GITHUB_OUTPUT fi - name: chart-testing (lint) diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index c009ab2de..1b922064a 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -2,9 +2,9 @@ name: "KICS" on: push: - branches: [main, master, develop] + branches: [main, releases] pull_request: - branches: [main, master, develop] + branches: [main, releases] workflow_dispatch: schedule: diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml new file mode 100644 index 000000000..24aaf2ff4 --- /dev/null +++ b/.github/workflows/publish-docker.yaml @@ -0,0 +1,83 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +name: "Create Docker images" + +on: + workflow_dispatch: + inputs: + namespace: + description: 'The namespace (=repo) in DockerHub' + required: false + default: "tractusx" + +concurrency: + # cancel only running jobs on pull requests + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + create-docker-image-controlplane: + name: "Create Docker Images for the ControlPlane" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: + - edc-runtime-memory + - edc-controlplane-memory-hashicorp-vault + - edc-controlplane-postgresql + - edc-controlplane-postgresql-hashicorp-vault + permissions: + contents: write + packages: write + steps: + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/publish-docker-image + with: + rootDir: edc-controlplane/${{ matrix.name }} + imagename: ${{ matrix.name }} + namespace: ${{ inputs.namespace }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} + + + create-docker-image-dataplane: + name: "Create Docker Images for the DataPlane" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: + - edc-dataplane-azure-vault + - edc-dataplane-hashicorp-vault + permissions: + contents: write + packages: write + steps: + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/publish-docker-image + with: + rootDir: edc-dataplane/${{ matrix.name }} + imagename: ${{ matrix.name }} + namespace: ${{ inputs.namespace }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 56148b82a..fbd0780ca 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -4,7 +4,7 @@ name: "Publish new release" on: pull_request: branches: - - main + - releases - support/* types: - closed @@ -37,7 +37,7 @@ jobs: name: Output release version id: release-version run: | - echo "::set-output name=RELEASE_VERSION::${{ env.RELEASE_VERSION }}" + echo "RELEASE_VERSION=${{ env.RELEASE_VERSION }}" >> $GITHUB_OUTPUT # Release: Maven Artifacts maven-release: @@ -54,31 +54,24 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - name: Checkout uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + uses: ./.github/actions/setup-java - name: Import GPG Key - uses: crazy-max/ghaction-import-gpg@v1 - env: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + uses: crazy-max/ghaction-import-gpg@v5 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} - name: Publish release version run: | echo "Publishing Version $(grep -e "version" gradle.properties | cut -f2 -d"=") to Github Packages" ./gradlew publishAllPublicationsToGithubPackagesRepository env: - #REPO: ${{ github.repository }} - REPO: "catenax-ng/product-edc" - GITHUB_PACKAGE_USERNAME: ${{ secrets.TEMP_GHPKG_USER }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.TEMP_GHPKG_PASSWORD }} + REPO: ${{ github.repository }} + GITHUB_PACKAGE_USERNAME: ${{ github.actor }} + GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} # Release: Helm Charts helm-release: @@ -97,7 +90,6 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - name: Checkout uses: actions/checkout@v3.3.0 with: fetch-depth: 0 @@ -128,7 +120,7 @@ jobs: git push origin gh-pages - # Release: GitHub tag & release; Merges back main into develop; Starts a new development cycle; + # Release: GitHub tag & release; Merges back releases into main; Starts a new development cycle; github-release: name: Publish new github release needs: [ release-version ] @@ -145,10 +137,9 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - name: Checkout uses: actions/checkout@v3.3.0 with: - # 0 to fetch the full history due to upcoming merge of main into develop branch + # 0 to fetch the full history due to upcoming merge of releases into main branch fetch-depth: 0 - name: Create Release Tag @@ -178,22 +169,17 @@ jobs: draft: false prerelease: false - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + uses: ./.github/actions/setup-java - - name: Merge main back into develop and set new snapshot version - if: github.event.pull_request.base.ref == 'main' + name: Merge releases back into main and set new snapshot version + if: github.event.pull_request.base.ref == 'releases' run: | # Prepare git env git config user.name "GitHub actions" git config user.email noreply@github.com - # Merge main into develop - git checkout develop && git merge -X theirs main --no-commit --no-ff + # Merge releases into main + git checkout main && git merge -X theirs releases --no-commit --no-ff # Extract release version IFS=. read -r RELEASE_VERSION_MAJOR RELEASE_VERSION_MINOR RELEASE_VERSION_PATCH<<<"${{ env.RELEASE_VERSION }}" @@ -204,8 +190,8 @@ jobs: # Persist the "version" in the gradle.properties sed -i "s/version=.*/version=$SNAPSHOT_VERSION/g" gradle.properties - # Commit and push to origin develop + # Commit and push to origin main git add gradle.properties git commit --message "Introduce new snapshot version $SNAPSHOT_VERSION" - git push origin develop + git push origin main diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index b82acaf66..2fe44c399 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -6,10 +6,10 @@ on: - cron: "0 0 * * *" workflow_dispatch: workflow_run: - workflows: ["Build"] + workflows: [ "Build" ] branches: - main - - develop + - releases - release/* - hotfix/* tags: @@ -24,11 +24,10 @@ jobs: outputs: value: ${{ steps.git-sha7.outputs.SHA7 }} steps: - - - name: Resolve git 7-chars sha + - name: Resolve git 7-chars sha id: git-sha7 run: | - echo "::set-output name=SHA7::${GITHUB_SHA::7}" + echo "SHA7=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT trivy-analyze-config: runs-on: ubuntu-latest @@ -37,11 +36,8 @@ jobs: contents: read security-events: write steps: - - - name: Checkout repository - uses: actions/checkout@v3.3.0 - - - name: Run Trivy vulnerability scanner in repo mode + - uses: actions/checkout@v3.3.0 + - name: Run Trivy vulnerability scanner in repo mode uses: aquasecurity/trivy-action@master with: scan-type: "config" @@ -51,8 +47,7 @@ jobs: format: "sarif" output: "trivy-results-config.sarif" severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab + - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v2 if: always() with: @@ -69,30 +64,35 @@ jobs: fail-fast: false # continue scanning other images although if the other has been vulnerable matrix: image: - - edc-controlplane-memory + - edc-runtime-memory - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault - edc-dataplane-azure-vault - edc-dataplane-hashicorp-vault steps: - - - name: Checkout - uses: actions/checkout@v3.3.0 - - - name: Run Trivy vulnerability scanner - if: always() + - uses: actions/checkout@v3.3.0 + + ## This step will fail if the docker images is not found + - name: "Check if image exists" + id: imageCheck + run: | + docker manifest inspect tractusx/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }} + continue-on-error: true + + ## the next two steps will only execute if the image exists check was successful + - name: Run Trivy vulnerability scanner + if: success() && steps.imageCheck.outcome != 'failure' uses: aquasecurity/trivy-action@master with: - image-ref: "ghcr.io/${{ github.repository }}/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }}" + image-ref: "tractusx/${{ matrix.image }}:sha-${{ needs.git-sha7.outputs.value }}" format: "sarif" output: "trivy-results-${{ matrix.image }}.sarif" exit-code: "1" severity: "CRITICAL,HIGH" timeout: "10m0s" - - - name: Upload Trivy scan results to GitHub Security tab - if: always() + - name: Upload Trivy scan results to GitHub Security tab + if: success() && steps.imageCheck.outcome != 'failure' uses: github/codeql-action/upload-sarif@v2 with: sarif_file: "trivy-results-${{ matrix.image }}.sarif" diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index 0bfaac8b5..486c53096 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -13,31 +13,21 @@ jobs: ORG_VERACODE_API_ID: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_ID }} ORG_VERACODE_API_KEY: ${{ steps.secret-presence.outputs.ORG_VERACODE_API_KEY }} steps: - - - name: Check whether secrets exist + - name: Check whether secrets exist id: secret-presence run: | - [ ! -z "${{ secrets.ORG_VERACODE_API_ID }}" ] && echo "::set-output name=ORG_VERACODE_API_ID::true" - [ ! -z "${{ secrets.ORG_VERACODE_API_KEY }}" ] && echo "::set-output name=ORG_VERACODE_API_KEY::true" + [ ! -z "${{ secrets.ORG_VERACODE_API_ID }}" ] && echo "ORG_VERACODE_API_ID=true" >> $GITHUB_OUTPUT + [ ! -z "${{ secrets.ORG_VERACODE_API_KEY }}" ] && echo "ORG_VERACODE_API_KEY=true" >> $GITHUB_OUTPUT exit 0 verify-formatting: runs-on: ubuntu-latest steps: - - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 with: fetch-depth: 0 - - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' - - - name: Verify proper formatting + - uses: ./.github/actions/setup-java + - name: Verify proper formatting run: ./gradlew spotlessCheck build-controlplane: @@ -49,36 +39,25 @@ jobs: fail-fast: false matrix: name: - - edc-controlplane-memory + - edc-runtime-memory - edc-controlplane-memory-hashicorp-vault - edc-controlplane-postgresql - edc-controlplane-postgresql-hashicorp-vault steps: # Set-Up - - - name: Checkout - uses: actions/checkout@v3.3.0 - - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/setup-java # Build - - - name: Build Controlplane + - name: Build Controlplane run: |- ./gradlew -p edc-controlplane/${{ matrix.name }} shadowJar env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: Tar gzip files for veracode upload + - name: Tar gzip files for veracode upload run: |- tar -czvf edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.tar.gz edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - - - name: Veracode Upload And Scan + - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY @@ -104,30 +83,19 @@ jobs: - edc-dataplane-hashicorp-vault steps: # Set-Up - - - name: Checkout - uses: actions/checkout@v3.3.0 - - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/setup-java # Build - - - name: Build Dataplane + - name: Build Dataplane run: |- ./gradlew -p edc-dataplane/${{ matrix.name }} shadowJar env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: Tar gzip files for veracode upload + - name: Tar gzip files for veracode upload run: |- tar -czvf edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.tar.gz edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - - - name: Veracode Upload And Scan + - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index bac515157..2cd0432f8 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -25,7 +25,7 @@ on: push: branches: - main - - develop + - releases tags: - '[0-9]+.[0-9]+.[0-9]+' release: @@ -34,13 +34,12 @@ on: pull_request: paths-ignore: - 'charts/**' - - 'docs/**' - - '**/*.md' branches: - '*' workflow_dispatch: concurrency: + # cancel older running jobs on the same branch group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -50,29 +49,19 @@ jobs: outputs: SONAR_TOKEN: ${{ steps.secret-presence.outputs.SONAR_TOKEN }} steps: - - - name: Check whether secrets exist + - name: Check whether secrets exist id: secret-presence run: | - [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "::set-output name=SONAR_TOKEN::true" + [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "SONAR_TOKEN=true" >> $GITHUB_OUTPUT exit 0 verify-formatting: runs-on: ubuntu-latest steps: - - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' - - - name: Verify proper formatting + - uses: ./.github/actions/setup-java + - name: Verify proper formatting run: ./gradlew spotlessCheck - name: Run Checkstyle @@ -83,82 +72,58 @@ jobs: markdown-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3.3.0 - name: Install mardkdownlint run: npm install -g markdownlint-cli2 - name: Run markdownlint run: | - markdownlint-cli2-config .markdownlint.yaml "**/*.md" + markdownlint-cli2-config .markdownlint.yaml "**/*.md" "#.github" unit-tests: runs-on: ubuntu-latest - needs: [verify-formatting] + needs: [ verify-formatting ] steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Run Unit tests run: ./gradlew test integration-tests: runs-on: ubuntu-latest - needs: [verify-formatting] + needs: [ verify-formatting ] steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Run Integration tests run: ./gradlew test -DincludeTags="ComponentTest" api-tests: runs-on: ubuntu-latest - needs: [verify-formatting] + needs: [ verify-formatting ] steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Run API tests run: ./gradlew test -DincludeTags="ApiTest" end-to-end-tests: runs-on: ubuntu-latest - needs: [verify-formatting] + needs: [ verify-formatting ] steps: - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' + - uses: ./.github/actions/setup-java - name: Run E2E tests - run: ./gradlew test -DincludeTags="EndToEndTest" + run: ./gradlew :edc-tests:runtime:build test -DincludeTags="EndToEndTest" sonar: needs: [ secret-presence, verify-formatting ] @@ -167,28 +132,18 @@ jobs: runs-on: ubuntu-latest steps: # Set-Up - - - name: Checkout - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.3.0 with: fetch-depth: 0 - - - name: Set up JDK 11 - uses: actions/setup-java@v3.10.0 - with: - java-version: '11' - distribution: 'temurin' - cache: 'gradle' - - - name: Cache SonarCloud packages + - uses: ./.github/actions/setup-java + - name: Cache SonarCloud packages uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar # Analyse - - - name: Build with Maven and analyze with Sonar + - name: Build with Maven and analyze with Sonar env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.markdownlint.yaml b/.markdownlint.yaml index ace38e3d4..d060f2264 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -19,6 +19,7 @@ "default": true # Do not restrict line length: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#MD013 "MD013": false +"MD034": # Allow same content on headlines on siblings: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#MD024 "MD024": "siblings_only": true diff --git a/CHANGELOG.md b/CHANGELOG.md index d3d528a32..dadff38f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.3.3] - 2023-04-19 + ## [0.3.2] - 2023-03-30 ### Fixed -- Fixed mutually-exclusive config values for Azure KeyVault +- Fixed mutually-exclusive config values for Azure KeyVault ## [0.3.1] - 2023-03-27 @@ -19,7 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Support unauthenticated access to the ObservabilityAPI (#126) +- Support unauthenticated access to the ObservabilityAPI (#126) ### Fixed @@ -30,205 +32,163 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). ### Added -- Add contract id to data source http call (#732) -- Support also support releases in ci pipeline -- Introduce typed object for oauth2 provisioning -- Add documentation -- Add test case -- Add client to omejdn -- add hydra deployment -- Configure dynamically HTTP Receiver callback endpoints. (#685) -- cp-adapter : code review, rollbacke name change (#664) -- Feature/cp adapter task 355 356 357 (#621) -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Local TXDC Setup Documentation (#618) -- Feature: Sftp Provisioner and Client (#554) - -- Add contract id to data source http call (#732) -- Support also support releases in ci pipeline -- Introduce typed object for oauth2 provisioning -- Add documentation -- Add test case -- Add client to omejdn -- add hydra deployment -- Configure dynamically HTTP Receiver callback endpoints. (#685) -- cp-adapter : code review, rollbacke name change (#664) -- Feature/cp adapter task 355 356 357 (#621) -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Local TXDC Setup Documentation (#618) -- Feature: Sftp Provisioner and Client (#554) +- Add contract id to data source http call (#732) +- Support also support releases in ci pipeline +- Introduce typed object for oauth2 provisioning +- Add documentation +- Add test case +- Add client to omejdn +- add hydra deployment +- Configure dynamically HTTP Receiver callback endpoints. (#685) +- cp-adapter : code review, rollbacke name change (#664) +- Feature/cp adapter task 355 356 357 (#621) +- Add Validity Mapping in ContractDefinitionStepDefs class +- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps +- Add validity attribute in class ContractDefinition +- Add Validity Mapping in ContractDefinitionStepDefs class +- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps +- Add validity attribute in class ContractDefinition +- Local TXDC Setup Documentation (#618) +- Feature: Sftp Provisioner and Client (#554) ### Changed -- Support horizontal edc scaling in cp adapter extension (#678) -- Use upstream jackson version (#741) -- Replace provision-oauth2 with data-plane-http-oauth2 -- docs: Update sample documentation (#671) -- chore: Disable build ci pipeline if just docu was updated (#705) -- Increase trivy timeout -- Remove not useful anymore custom-jsonld extension (#683) -- update setup docu (#654) -- remove trailing slash (#652) -- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) -- Feature/set charts deprecated (#628) -- update setup docu (#627) -- Feature/update txdc deployment downward capabilities (#625) -- remove git submodule (#619) -- Feature/update postman (#624) -- update control plane docu (#623) -- update postgresql version in Chart.yaml supporting-infrastructure (#622) -- update link to edc logo in README.md (#612) -- update description of supporting infrastructure deployment (#616) - -- Support horizontal edc scaling in cp adapter extension (#678) -- Use upstream jackson version (#741) -- Replace provision-oauth2 with data-plane-http-oauth2 -- docs: Update sample documentation (#671) -- chore: Disable build ci pipeline if just docu was updated (#705) -- Increase trivy timeout -- Remove not useful anymore custom-jsonld extension (#683) -- update setup docu (#654) -- remove trailing slash (#652) -- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) -- Feature/set charts deprecated (#628) -- update setup docu (#627) -- Feature/update txdc deployment downward capabilities (#625) -- remove git submodule (#619) -- Feature/update postman (#624) -- update control plane docu (#623) -- update postgresql version in Chart.yaml supporting-infrastructure (#622) -- update link to edc logo in README.md (#612) -- update description of supporting infrastructure deployment (#616) +- Support horizontal edc scaling in cp adapter extension (#678) +- Use upstream jackson version (#741) +- Replace provision-oauth2 with data-plane-http-oauth2 +- docs: Update sample documentation (#671) +- chore: Disable build ci pipeline if just docu was updated (#705) +- Increase trivy timeout +- Remove not useful anymore custom-jsonld extension (#683) +- update setup docu (#654) +- remove trailing slash (#652) +- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) +- Feature/set charts deprecated (#628) +- update setup docu (#627) +- Feature/update txdc deployment downward capabilities (#625) +- remove git submodule (#619) +- Feature/update postman (#624) +- update control plane docu (#623) +- update postgresql version in Chart.yaml supporting-infrastructure (#622) +- update link to edc logo in README.md (#612) +- update description of supporting infrastructure deployment (#616) ### Fixed -- bugfix: Fix slow AES encryption (#746) -- Fix typo in tractusx-connector values.yaml comment -- Fix not working docu link in README.md -- Fix typo in control-plane adapter README - -- bugfix: Fix slow AES encryption (#746) -- Fix typo in tractusx-connector values.yaml comment -- Fix not working docu link in README.md -- Fix typo in control-plane adapter README +- bugfix: Fix slow AES encryption (#746) +- Fix typo in tractusx-connector values.yaml comment +- Fix not working docu link in README.md +- Fix typo in control-plane adapter README ### Dependency updates -- Bump EDC to 20220220 (#767) -- Bump alpine (#749) -- Bump alpine (#750) -- Bump alpine (#752) -- Bump alpine in /edc-controlplane/edc-controlplane-memory/src/main/docker (#753) -- Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) -- Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) -- Bump s3 from 2.19.33 to 2.20.0 -- Bump s3 from 2.19.27 to 2.19.33 -- Bump jaxb-runtime from 4.0.1 to 4.0.2 -- Bump spotless-maven-plugin from 2.31.0 to 2.32.0 -- Bump postgresql from 42.5.1 to 42.5.3 -- Bump nimbus-jose-jwt from 9.30 to 9.30.1 -- Bump lombok from 1.18.24 to 1.18.26 -- Bump flyway-core from 9.12.0 to 9.14.1 -- Bump jackson-bom from 2.14.0-rc2 to 2.14.2 -- Bump cucumber.version from 7.11.0 to 7.11.1 -- Bump azure-sdk-bom from 1.2.8 to 1.2.9 -- Bump mockito-bom from 5.0.0 to 5.1.1 -- Bump edc version to 0.0.1-20230131-SNAPSHOT -- Bump s3 from 2.19.18 to 2.19.27 -- Bump docker/build-push-action from 3 to 4 -- Bump nimbus-jose-jwt from 9.29 to 9.30 -- Bump spotless-maven-plugin from 2.30.0 to 2.31.0 -- Bump nimbus-jose-jwt from 9.28 to 9.29 -- Bump mockito-bom from 4.11.0 to 5.0.0 -- Bump edc version to 0.0.1-20230125-SNAPSHOT -- Bump flyway-core from 9.11.0 to 9.12.0 -- Bump s3 from 2.19.15 to 2.19.18 (#684) -- Bump mikefarah/yq from 4.30.6 to 4.30.8 (#682) -- Bump spotless-maven-plugin from 2.29.0 to 2.30.0 -- Bump edc version to 0.0.1-20230115-SNAPSHOT -- Bump cucumber.version from 7.10.1 to 7.11.0 (#672) -- Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#669) -- Bump s3 from 2.19.11 to 2.19.15 (#668) -- Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) -- Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) -- Bump alpine in /edc-controlplane/edc-controlplane-memory/src/main/docker (#659) -- Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) -- Bump alpine (#658) -- Bump alpine (#661) -- Bump alpine (#662) -- Bump azure/setup-kubectl from 3.1 to 3.2 (#655) -- Bump junit-bom from 5.9.1 to 5.9.2 (#657) -- Bump s3 from 2.19.2 to 2.19.11 (#648) -- Bump actions/checkout from 3.2.0 to 3.3.0 (#647) -- Bump flyway-core from 9.10.2 to 9.11.0 (#646) -- Bump spotless-maven-plugin from 2.28.0 to 2.29.0 (#641) -- Bump mockito-bom from 4.10.0 to 4.11.0 (#637) -- Bump flyway-core from 9.10.1 to 9.10.2 (#632) -- Bump s3 from 2.19.1 to 2.19.2 (#631) -- Bump s3 from 2.18.41 to 2.19.1 (#626) -- Bump mikefarah/yq from 4.30.5 to 4.30.6 (#613) -- Bump cucumber.version from 7.10.0 to 7.10.1 (#614) -- Bump s3 from 2.18.40 to 2.18.41 (#615) -- Bump azure/setup-helm from 3.4 to 3.5 (#596) -- Bump actions/checkout from 3.1.0 to 3.2.0 (#598) -- Bump mockito-bom from 4.9.0 to 4.10.0 (#607) -- Bump s3 from 2.18.39 to 2.18.40 (#609) -- Bump flyway-core from 9.10.0 to 9.10.1 (#610) -- Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) -- Bump s3 from 2.18.35 to 2.18.39 (#606) +- Bump EDC to 20220220 (#767) +- Bump alpine (#749) +- Bump alpine (#750) +- Bump alpine (#752) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#753) +- Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) +- Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) +- Bump s3 from 2.19.33 to 2.20.0 +- Bump s3 from 2.19.27 to 2.19.33 +- Bump jaxb-runtime from 4.0.1 to 4.0.2 +- Bump spotless-maven-plugin from 2.31.0 to 2.32.0 +- Bump postgresql from 42.5.1 to 42.5.3 +- Bump nimbus-jose-jwt from 9.30 to 9.30.1 +- Bump lombok from 1.18.24 to 1.18.26 +- Bump flyway-core from 9.12.0 to 9.14.1 +- Bump jackson-bom from 2.14.0-rc2 to 2.14.2 +- Bump cucumber.version from 7.11.0 to 7.11.1 +- Bump azure-sdk-bom from 1.2.8 to 1.2.9 +- Bump mockito-bom from 5.0.0 to 5.1.1 +- Bump edc version to 0.0.1-20230131-SNAPSHOT +- Bump s3 from 2.19.18 to 2.19.27 +- Bump docker/build-push-action from 3 to 4 +- Bump nimbus-jose-jwt from 9.29 to 9.30 +- Bump spotless-maven-plugin from 2.30.0 to 2.31.0 +- Bump nimbus-jose-jwt from 9.28 to 9.29 +- Bump mockito-bom from 4.11.0 to 5.0.0 +- Bump edc version to 0.0.1-20230125-SNAPSHOT +- Bump flyway-core from 9.11.0 to 9.12.0 +- Bump s3 from 2.19.15 to 2.19.18 (#684) +- Bump mikefarah/yq from 4.30.6 to 4.30.8 (#682) +- Bump spotless-maven-plugin from 2.29.0 to 2.30.0 +- Bump edc version to 0.0.1-20230115-SNAPSHOT +- Bump cucumber.version from 7.10.1 to 7.11.0 (#672) +- Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#669) +- Bump s3 from 2.19.11 to 2.19.15 (#668) +- Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) +- Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#659) +- Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) +- Bump alpine (#658) +- Bump alpine (#661) +- Bump alpine (#662) +- Bump azure/setup-kubectl from 3.1 to 3.2 (#655) +- Bump junit-bom from 5.9.1 to 5.9.2 (#657) +- Bump s3 from 2.19.2 to 2.19.11 (#648) +- Bump actions/checkout from 3.2.0 to 3.3.0 (#647) +- Bump flyway-core from 9.10.2 to 9.11.0 (#646) +- Bump spotless-maven-plugin from 2.28.0 to 2.29.0 (#641) +- Bump mockito-bom from 4.10.0 to 4.11.0 (#637) +- Bump flyway-core from 9.10.1 to 9.10.2 (#632) +- Bump s3 from 2.19.1 to 2.19.2 (#631) +- Bump s3 from 2.18.41 to 2.19.1 (#626) +- Bump mikefarah/yq from 4.30.5 to 4.30.6 (#613) +- Bump cucumber.version from 7.10.0 to 7.10.1 (#614) +- Bump s3 from 2.18.40 to 2.18.41 (#615) +- Bump azure/setup-helm from 3.4 to 3.5 (#596) +- Bump actions/checkout from 3.1.0 to 3.2.0 (#598) +- Bump mockito-bom from 4.9.0 to 4.10.0 (#607) +- Bump s3 from 2.18.39 to 2.18.40 (#609) +- Bump flyway-core from 9.10.0 to 9.10.1 (#610) +- Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) +- Bump s3 from 2.18.35 to 2.18.39 (#606) ## [0.1.6] - 2023-02-20 ### Fixed -- SQL leakage issue -- Catalog pagination +- SQL leakage issue +- Catalog pagination ## [0.1.5] - 2023-02-13 ### Fixed -- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug -- Data Encryption extension: fixed usage of a blocking algorithm +- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug +- Data Encryption extension: fixed usage of a blocking algorithm ## [0.1.2] - 2022-09-30 ### Added -- Introduced DEPENDENCIES file +- Introduced DEPENDENCIES file ### Changed -- Moved helm charts from `deployment/helm` to `charts` -- Replaced distroless image with alpine in all docker images -- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` +- Moved helm charts from `deployment/helm` to `charts` +- Replaced distroless image with alpine in all docker images +- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` ## [0.1.1] - 2022-09-04 -**Important Note**: Please consolidate the migration documentation before updating your connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). +**Important Note**: Please consolidate the migration documentation before updating your +connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). ### Added -- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) +- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) ### Changed -- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) -- Helm Charts: TLS secret name is now configurable +- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) +- Helm Charts: TLS secret name is now configurable ### Fixed -- Connectors with Azure Vault extension are now starting again [link](https://github.com/eclipse-edc/Connector/issues/1892) +- Connectors with Azure Vault extension are now starting + again [link](https://github.com/eclipse-edc/Connector/issues/1892) ## [0.1.0] - 2022-08-19 @@ -237,64 +197,74 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Added -- Control-Plane extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) - - run the EDC with multiple data planes at once -- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) - - add data plane instances to the control plane by configuration -- Data-Plane extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) - - transfer from and to AWS S3 buckets -- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) - - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) +- Control-Plane + extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) + - run the EDC with multiple data planes at once +- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) + - add data plane instances to the control plane by configuration +- Data-Plane + extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) + - transfer from and to AWS S3 buckets +- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) + - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) ### Changed -- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) -- EDC has been updated to version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - implications to the behavior of the connector have been covered in the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) +- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) +- EDC has been updated to + version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - + implications to the behavior of the connector have been covered in + the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) ### Fixed -- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) -- Deletion of Asset becomes impossible when Contract Negotiation exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) -- Deletion of Policy becomes impossible when Contract Definition exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving + offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) +- Deletion of Asset becomes impossible when Contract Negotiation + exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) +- Deletion of Policy becomes impossible when Contract Definition + exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) ## [0.0.6] - 2022-07-29 ### Fixed -- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) library +- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath + issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) + library ## [0.0.5] - 2022-07-28 ### Added -- EDC Health Checks for HashiCorp Vault +- EDC Health Checks for HashiCorp Vault ### Changed -- BusinessPartnerNumber constraint supports List structure -- Helm: Confidential EDC settings can be set using k8s secrets -- HashiCorp Vault API path configurable +- BusinessPartnerNumber constraint supports List structure +- Helm: Confidential EDC settings can be set using k8s secrets +- HashiCorp Vault API path configurable ## [0.0.4] - 2022-06-27 ### Added -- HashiCorp Vault Extension -- Control Plane with HashiCorp Vault and PostgreSQL support +- HashiCorp Vault Extension +- Control Plane with HashiCorp Vault and PostgreSQL support ### Changed -- Release Workflow now publishes EDC Extensions as Maven Artifacts +- Release Workflow now publishes EDC Extensions as Maven Artifacts ### Fixed -- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 - contract offers max. +- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 + contract offers max. ### Removed -- CosmosDB Control Plane -- Control API Extension for all Control Planes +- CosmosDB Control Plane +- Control API Extension for all Control Planes ## [0.0.3] - 2022-05-23 @@ -302,7 +272,9 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ## [0.0.1] - 2022-05-13 -[Unreleased]: https://github.com/catenax-ng/tx-tractusx-edc/compare/0.3.2...HEAD +[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.3...HEAD + +[0.3.3]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.2...0.3.3 [0.3.2]: https://github.com/catenax-ng/tx-tractusx-edc/compare/0.3.1...0.3.2 diff --git a/NOTICE.md b/NOTICE.md index 24a59602c..4223c64f3 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -26,6 +26,7 @@ SPDX-License-Identifier: Apache-2.0 ## Source Code +The project maintains the following source code repositories in the GitHub organization : * diff --git a/README.md b/README.md index 566e42f5a..d1c6a0422 100644 --- a/README.md +++ b/README.md @@ -16,24 +16,25 @@ Please also refer to: ## About The Project -The project provides pre-built control- and data-plane [docker](https://www.docker.com/) images and [helm](https://helm.sh/) charts of the [Eclipse DataSpaceConnector Project](https://github.com/eclipse-edc/Connector). +The project provides pre-built control- and data-plane [docker](https://www.docker.com/) images +and [helm](https://helm.sh/) charts of +the [Eclipse DataSpaceConnector Project](https://github.com/eclipse-edc/Connector). ## Inventory -The eclipse data space connector is split up into Control-Plane and Data-Plane, whereas the Control-Plane functions as administration layer -and has responsibility of resource management, contract negotiation and administer data transfer. +The eclipse data space connector is split up into Control-Plane and Data-Plane, whereas the Control-Plane functions as +administration layer and has responsibility of resource management, contract negotiation and administer data transfer. The Data-Plane does the heavy lifting of transferring and receiving data streams. Depending on your environment there are different derivatives of the control-plane prepared: -- [edc-controlplane-memory](edc-controlplane/edc-controlplane-memory) with dependency onto - - [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) - [edc-controlplane-postgresql](edc-controlplane/edc-controlplane-postgresql) with dependency onto - [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/#product-overview) - [PostgreSQL 8.2 or newer](https://www.postgresql.org/) -- [edc-controlplane-postgresql-hashicorp-vault](edc-controlplane/edc-controlplane-postgresql-hashicorp-vault) with dependency onto +- [edc-controlplane-postgresql-hashicorp-vault](edc-controlplane/edc-controlplane-postgresql-hashicorp-vault) with + dependency onto - [Hashicorp Vault](https://www.vaultproject.io/) - -[PostgreSQL 8.2 or newer](https://www.postgresql.org/) + - [PostgreSQL 8.2 or newer](https://www.postgresql.org/) Derivatives of the Data-Plane can be found here @@ -42,6 +43,10 @@ Derivatives of the Data-Plane can be found here - [edc-dataplane-hashicorp-vault](edc-dataplane/edc-dataplane-hashicorp-vault) with dependency onto - [Hashicorp Vault](https://www.vaultproject.io/) +For testing/development purposes: + +- [edc-runtime-memory](edc-controlplane/edc-runtime-memory) + ## Getting Started ### Build @@ -54,15 +59,24 @@ Build Tractus-X EDC together with its Container Images ## License -Distributed under the Apache 2.0 License. See [LICENSE](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) for more information. +Distributed under the Apache 2.0 License. +See [LICENSE](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) for more information. + [contributors-shield]: https://img.shields.io/github/contributors/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge + [contributors-url]: https://github.com/eclipse-tractusx/tractusx-edc/graphs/contributors + [stars-shield]: https://img.shields.io/github/stars/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge + [stars-url]: https://github.com/eclipse-tractusx/tractusx-edc/stargazers + [license-shield]: https://img.shields.io/github/license/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge + [license-url]: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE + [release-shield]: https://img.shields.io/github/v/release/eclipse-tractusx/tractusx-edc.svg?style=for-the-badge + [release-url]: https://github.com/eclipse-tractusx/tractusx-edc/releases diff --git a/build.gradle.kts b/build.gradle.kts index a9e1f992d..1f545aea5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,10 +5,10 @@ plugins { `java-library` `maven-publish` `jacoco-report-aggregation` - id("io.freefair.lombok") version "6.6.2" - id("com.diffplug.spotless") version "6.15.0" - id("com.github.johnrengelman.shadow") version "8.0.0" - id("com.bmuschko.docker-remote-api") version "9.2.1" + id("io.freefair.lombok") version "8.0.1" + id("com.diffplug.spotless") version "6.18.0" + id("com.github.johnrengelman.shadow") version "8.1.1" + id("com.bmuschko.docker-remote-api") version "9.3.1" id("org.sonarqube") version "4.0.0.2929" } @@ -52,7 +52,7 @@ allprojects { } dependencies { implementation("org.projectlombok:lombok:1.18.26") - implementation("org.slf4j:slf4j-api:2.0.5") + implementation("org.slf4j:slf4j-api:2.0.7") // this is used to counter version conflicts between the JUnit version pulled in by the plugin, // and the one expected by IntelliJ testImplementation(platform("org.junit:junit-bom:5.9.2")) @@ -141,8 +141,9 @@ subprojects { dockerFile.set(file("${project.projectDir}/src/main/docker/Dockerfile")) images.add("${project.name}:${project.version}") images.add("${project.name}:latest") - // uncomment the following line if building on Apple Silicon - // platform.set("linux/x86_64") + // specify platform with the -Dplatform flag: + if (System.getProperty("platform") != null) + platform.set(System.getProperty("platform")) buildArgs.put("JAR", "build/libs/${project.name}.jar") inputDir.set(file(project.projectDir)) } diff --git a/charts/edc-controlplane/Chart.yaml b/charts/edc-controlplane/Chart.yaml deleted file mode 100644 index ffd77bd4d..000000000 --- a/charts/edc-controlplane/Chart.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v2 -name: edc-controlplane -description: >- - EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers -home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-controlplane -type: application -appVersion: "0.3.2" -version: 0.3.2 -deprecated: true -maintainers: [] -sources: - - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-controlplane diff --git a/charts/edc-controlplane/LICENSE b/charts/edc-controlplane/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/charts/edc-controlplane/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/charts/edc-controlplane/README.md b/charts/edc-controlplane/README.md deleted file mode 100644 index 9984db480..000000000 --- a/charts/edc-controlplane/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# edc-controlplane - -> **:exclamation: This Helm Chart is deprecated!** - -![Version: 0.3.2](https://img.shields.io/badge/Version-0.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.2](https://img.shields.io/badge/AppVersion-0.3.2-informational?style=flat-square) - -EDC Control-Plane - The Eclipse DataSpaceConnector administration layer with responsibility of resource management and govern contracts and data transfers - -**Homepage:** - -## TL;DR - -```shell -helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-controlplane --version 0.3.2 -``` - -## Source Code - -* - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | -| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | -| autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | -| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.api.auth.key=\n# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.dataplane.token.validation.endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.audience=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.proxy.endpoint=\n# edc.transfer.proxy.token.validity.seconds=\n# edc.transfer.proxy.token.signer.privatekey.alias=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins=\n# ids.webhook.address="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-edc/Connector) | -| customLabels | object | `{}` | Additional custom Labels to add | -| edc.endpoints.control.path | string | `"/api/controlplane/control"` | The path mapping the "control" api is going to be exposed at | -| edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | -| edc.endpoints.data.path | string | `"/data"` | The path mapping the "data" management api is going to be exposed at | -| edc.endpoints.data.port | string | `"8181"` | The network port, which the "data" management api is going to be exposed by the container, pod and service | -| edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed at | -| edc.endpoints.default.port | string | `"8080"` | The network port, which the "default" api is going to be exposed by the container, pod and service | -| edc.endpoints.ids.path | string | `"/api/v1/ids"` | The path mapping the "ids" multipart api is going to be exposed at | -| edc.endpoints.ids.port | string | `"8282"` | The network port, which the "ids" multipart api is going to be exposed by the container, pod and service | -| edc.endpoints.metrics.path | string | `"/metrics"` | The path mapping the prometheus metrics are going to be exposed at | -| edc.endpoints.metrics.port | string | `"9090"` | The network port, which the prometheus metrics are going to be exposed by the container, pod and service | -| edc.endpoints.validation.path | string | `"/validation"` | The path mapping the "validation" api is going to be exposed at | -| edc.endpoints.validation.port | string | `"8182"` | The network port, which the "validation" api is going to be exposed by the container, pod and service | -| env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | -| envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | -| fullnameOverride | string | `""` | Overrides the releases full name | -| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault"` | Which derivate of the edc control-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-memory] | -| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | -| imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | -| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | -| ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | -| ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| ingresses[0].enabled | bool | `true` | | -| ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | -| ingresses[0].hostname | string | `"edc-controlplane.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | -| ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | -| ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| ingresses[1].enabled | bool | `false` | | -| ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | -| ingresses[1].hostname | string | `"edc-controlplane.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | -| livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| logging.properties | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) | -| nameOverride | string | `""` | Overrides the charts name | -| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | -| opentelemetry.properties | string | `"otel.javaagent.enabled=true\notel.javaagent.debug=false"` | opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) | -| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | -| podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | -| podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | -| podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | -| podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | -| readinessProbe.enabled | bool | `true` | Whether to enable kubernetes readiness-probes | -| replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | -| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | -| securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | -| securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | -| securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | -| securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | -| securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | -| service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | -| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | -| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | -| startupProbe.enabled | bool | `true` | Whether to enable kubernetes startup-probes | -| startupProbe.failureThreshold | int | `12` | Minimum consecutive failures for the probe to be considered failed after having succeeded | -| startupProbe.initialDelaySeconds | int | `10` | Number of seconds after the container has started before liveness probes are initiated. | -| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | -| volumeMounts | list | `[]` | Additional volumeMounts to the controlplane main container | -| volumes | list | `[]` | Additional volumes to the controlplane pod | - ----------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/edc-controlplane/templates/NOTES.txt b/charts/edc-controlplane/templates/NOTES.txt deleted file mode 100644 index 6758c6bdf..000000000 --- a/charts/edc-controlplane/templates/NOTES.txt +++ /dev/null @@ -1,74 +0,0 @@ - -CHART NAME: {{ .Chart.Name }} -CHART VERSION: {{ .Chart.Version }} -APP VERSION: {{ .Chart.AppVersion }} - -Logs can be accessed by running this command: - - kubectl logs --tail 100 -f \ - --namespace {{ .Release.Namespace }} \ - -l "app.kubernetes.io/name={{ include "edc-controlplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" - -{{- if .Values.ingresses }} - -Following ingress URLS are available: - {{- $edcEndpoints := .Values.edc.endpoints }} - {{- range .Values.ingresses }} - {{- if .enabled }} - {{- $ingressEdcEndpoints := .endpoints }} - {{- $hostname := .hostname }} - {{- $tls := .tls }} - {{- range $name, $mapping := $edcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - Visit http{{ if $tls }}s{{ end }}://{{ $hostname }}{{ $mapping.path }} to access the {{ $name }} api - {{- end }} - {{- end }} - {{- end }} - {{- end }} - -{{- else if contains "NodePort" .Values.service.type }} -Get the application URLs by running these commands: - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - - export NODE_PORT_DEFAULT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_DATA=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_VALIDATION=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[2].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_CONTROL=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[3].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_IDS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[4].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - export NODE_PORT_METRICS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[5].nodePort}" services {{ include "edc-controlplane.fullname" . }}}") - - echo "Visit http://$NODE_IP:$NODE_PORT_DEFAULT to access the default api" - echo "Visit http://$NODE_IP:$NODE_PORT_DATA to access the data management api" - echo "Visit http://$NODE_IP:$NODE_PORT_VALIDATION to access the data transfer validation api" - echo "Visit http://$NODE_IP:$NODE_PORT_CONTROL to access the control api" - echo "Visit http://$NODE_IP:$NODE_PORT_IDS to access the IDS api" - echo "Visit http://$NODE_IP:$NODE_PORT_METRICS to access the metrics api" - -{{- else if contains "ClusterIP" .Values.service.type }} -Get the application URL by running these commands: - - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "edc-controlplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - - export CONTAINER_PORT_DEFAULT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - export CONTAINER_PORT_DATA=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[1].containerPort}") - export CONTAINER_PORT_VALIDATION=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[2].containerPort}") - export CONTAINER_PORT_CONTROL=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[3].containerPort}") - export CONTAINER_PORT_IDS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[4].containerPort}") - export CONTAINER_PORT_METRICS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[5].containerPort}") - - echo "Visit http://127.0.0.1:8080 to access the default api" - echo "Visit http://127.0.0.1:8182 to access the data management api" - echo "Visit http://127.0.0.1:8182 to access the data transfer validation api" - echo "Visit http://127.0.0.1:9999 to access the control api" - echo "Visit http://127.0.0.1:8282 to access the IDS api" - echo "Visit http://127.0.0.1:9090 to access the metrics api" - - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME \ - 8080:$CONTAINER_PORT_DEFAULT \ - 8182:$CONTAINER_PORT_DATA \ - 8182:$CONTAINER_PORT_VALIDATION \ - 9999:$CONTAINER_PORT_CONTROL \ - 8282:$CONTAINER_PORT_IDS \ - 9090:$CONTAINER_PORT_METRICS - -{{- end }} diff --git a/charts/edc-controlplane/templates/_helpers.tpl b/charts/edc-controlplane/templates/_helpers.tpl deleted file mode 100644 index 272a0f27d..000000000 --- a/charts/edc-controlplane/templates/_helpers.tpl +++ /dev/null @@ -1,72 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "edc-controlplane.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "edc-controlplane.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "edc-controlplane.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "edc-controlplane.labels" -}} -helm.sh/chart: {{ include "edc-controlplane.chart" . }} -{{ include "edc-controlplane.selectorLabels" . }} -{{ include "edc-controlplane.customLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "edc-controlplane.selectorLabels" -}} -app.kubernetes.io/name: {{ include "edc-controlplane.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Custom labels -*/}} -{{- define "edc-controlplane.customLabels" -}} -{{- with .Values.customLabels }} -{{ toYaml . }} -{{- end }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "edc-controlplane.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "edc-controlplane.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/charts/edc-controlplane/templates/configmap.yaml b/charts/edc-controlplane/templates/configmap.yaml deleted file mode 100644 index 863ac5e83..000000000 --- a/charts/edc-controlplane/templates/configmap.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "edc-controlplane.fullname" . }}-configmap - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -data: - configuration.properties: |- - web.http.default.port={{ .Values.edc.endpoints.default.port }} - web.http.default.path={{ .Values.edc.endpoints.default.path }} - web.http.data.port={{ .Values.edc.endpoints.data.port }} - web.http.data.path={{ .Values.edc.endpoints.data.path }} - web.http.validation.port={{ .Values.edc.endpoints.validation.port }} - web.http.validation.path={{ .Values.edc.endpoints.validation.path }} - web.http.control.port={{ .Values.edc.endpoints.control.port }} - web.http.control.path={{ .Values.edc.endpoints.control.path }} - web.http.ids.port={{ .Values.edc.endpoints.ids.port }} - web.http.ids.path={{ .Values.edc.endpoints.ids.path }} - {{- .Values.configuration.properties | nindent 4 }} - - opentelemetry.properties: |- - {{- .Values.opentelemetry.properties | nindent 4 }} - - logging.properties: |- - {{- .Values.logging.properties | nindent 4 }} diff --git a/charts/edc-controlplane/templates/deployment.yaml b/charts/edc-controlplane/templates/deployment.yaml deleted file mode 100644 index 4fd762d0b..000000000 --- a/charts/edc-controlplane/templates/deployment.yaml +++ /dev/null @@ -1,154 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "edc-controlplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "edc-controlplane.selectorLabels" . | nindent 6 }} - template: - metadata: - annotations: - checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - checksum/env-config: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} - {{- with .Values.podAnnotations }} - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "edc-controlplane.selectorLabels" . | nindent 8 }} - spec: - {{- if .Values.imagePullSecret.dockerconfigjson }} - imagePullSecrets: - - name: {{ include "edc-controlplane.fullname" . }}-imagepullsecret - {{- else }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- end }} - serviceAccountName: {{ include "edc-controlplane.serviceAccountName" . }} - automountServiceAccountToken: {{ if .Values.automountServiceAccountToken }}true{{ else }}false{{ end }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: default - containerPort: {{ .Values.edc.endpoints.default.port }} - protocol: TCP - - name: data - containerPort: {{ .Values.edc.endpoints.data.port }} - protocol: TCP - - name: validation - containerPort: {{ .Values.edc.endpoints.validation.port }} - protocol: TCP - - name: control - containerPort: {{ .Values.edc.endpoints.control.port }} - protocol: TCP - - name: ids - containerPort: {{ .Values.edc.endpoints.ids.port }} - protocol: TCP - - name: metrics - containerPort: {{ .Values.edc.endpoints.metrics.port }} - protocol: TCP - {{- if .Values.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/liveness - port: default - {{- end }} - {{- if .Values.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/readiness - port: default - {{- end }} - {{- if .Values.startupProbe.enabled }} - startupProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/startup - port: default - failureThreshold: {{ .Values.startupProbe.failureThreshold }} - initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} - {{- end }} - envFrom: - - configMapRef: - name: {{ include "edc-controlplane.fullname" . }}-env - {{- if .Values.envSecretName }} - - secretRef: - name: {{ .Values.envSecretName | quote }} - {{- end }} - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: configuration - mountPath: /app/configuration.properties - subPath: configuration.properties - - name: configuration - mountPath: /app/opentelemetry.properties - subPath: opentelemetry.properties - - name: configuration - mountPath: /app/logging.properties - subPath: logging.properties - {{- with .Values.volumeMounts }} - {{- toYaml . | nindent 12 }} - {{- end }} - volumes: - - name: configuration - configMap: - name: {{ include "edc-controlplane.fullname" . }}-configmap - items: - - key: configuration.properties - path: configuration.properties - - key: opentelemetry.properties - path: opentelemetry.properties - - key: logging.properties - path: logging.properties - {{- with .Values.volumes }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/charts/edc-controlplane/templates/hpa.yaml b/charts/edc-controlplane/templates/hpa.yaml deleted file mode 100644 index bc75d097a..000000000 --- a/charts/edc-controlplane/templates/hpa.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.autoscaling.enabled }} ---- -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "edc-controlplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "edc-controlplane.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/edc-controlplane/templates/imagepullsecret.yaml b/charts/edc-controlplane/templates/imagepullsecret.yaml deleted file mode 100644 index 6b6e29ace..000000000 --- a/charts/edc-controlplane/templates/imagepullsecret.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.imagePullSecret.dockerconfigjson }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "edc-controlplane.fullname" . }}-imagepullsecret - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -data: - .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} -type: kubernetes.io/dockerconfigjson -{{- end }} diff --git a/charts/edc-controlplane/templates/ingress.yaml b/charts/edc-controlplane/templates/ingress.yaml deleted file mode 100644 index cb58b5ac9..000000000 --- a/charts/edc-controlplane/templates/ingress.yaml +++ /dev/null @@ -1,100 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG - # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH - # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation - # - # See the NOTICE file(s) distributed with this work for additional - # information regarding copyright ownership. - # - # 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 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - # License for the specific language governing permissions and limitations - # under the License. - # - # SPDX-License-Identifier: Apache-2.0 - # - -{{- $fullName := include "edc-controlplane.fullname" . }} -{{- $labels := include "edc-controlplane.labels" . | nindent 4 }} -{{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} -{{- $edcEndpoints := .Values.edc.endpoints }} -{{- $namespace := .Release.Namespace }} -{{- range .Values.ingresses }} -{{- if and .enabled .endpoints }} -{{- $ingressName := printf "%s-%s" $fullName .hostname }} ---- -{{- if semverCompare ">=1.19-0" $gitVersion }} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" $gitVersion }} -apiVersion: networking.k8s.io/v1beta1 -{{- else }} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $ingressName }} - namespace: {{ $namespace | default "default" | quote }} - labels: - {{- $labels | nindent 2 }} - annotations: - {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} - {{- if not (hasKey .annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .annotations "kubernetes.io/ingress.class" .className}} - {{- end }} - {{- end }} - {{- if .certManager }} - {{- if .certManager.issuer }} - {{- $_ := set .annotations "cert-manager.io/issuer" .certManager.issuer}} - {{- end }} - {{- if .certManager.clusterIssuer }} - {{- $_ := set .annotations "cert-manager.io/cluster-issuer" .certManager.clusterIssuer}} - {{- end }} - {{- end }} - {{- with .annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .className (semverCompare ">=1.18-0" $gitVersion) }} - ingressClassName: {{ .className }} - {{- end }} - {{- if .hostname }} - {{- if .tls.enabled }} - tls: - - hosts: - - {{ .hostname }} - {{- if .tls.secretName }} - secretName: {{ .tls.secretName }} - {{- else }} - secretName: {{ $ingressName }}-tls - {{- end }} - {{- end }} - rules: - - host: {{ .hostname }} - http: - paths: - {{- $ingressEdcEndpoints := .endpoints }} - {{- range $name, $mapping := $edcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - - path: {{ $mapping.path }} - pathType: Prefix - backend: - {{- if semverCompare ">=1.19-0" $gitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $mapping.port }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $mapping.port }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} -{{- end }}{{- /* end: if .enabled */}} -{{- end }}{{- /* end: range .Values.ingresses */}} diff --git a/charts/edc-controlplane/templates/service.yaml b/charts/edc-controlplane/templates/service.yaml deleted file mode 100644 index 18bc8bd55..000000000 --- a/charts/edc-controlplane/templates/service.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "edc-controlplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.edc.endpoints.default.port }} - targetPort: default - protocol: TCP - name: default - - port: {{ .Values.edc.endpoints.control.port }} - targetPort: control - protocol: TCP - name: control - - port: {{ .Values.edc.endpoints.data.port }} - targetPort: data - protocol: TCP - name: data - - port: {{ .Values.edc.endpoints.validation.port }} - targetPort: validation - protocol: TCP - name: validation - - port: {{ .Values.edc.endpoints.ids.port }} - targetPort: ids - protocol: TCP - name: ids - - port: {{ .Values.edc.endpoints.metrics.port }} - targetPort: metrics - protocol: TCP - name: metrics - selector: - {{- include "edc-controlplane.selectorLabels" . | nindent 4 }} diff --git a/charts/edc-controlplane/templates/serviceaccount.yaml b/charts/edc-controlplane/templates/serviceaccount.yaml deleted file mode 100644 index 1f9d5045b..000000000 --- a/charts/edc-controlplane/templates/serviceaccount.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.serviceAccount.create -}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "edc-controlplane.serviceAccountName" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/charts/edc-controlplane/values.yaml b/charts/edc-controlplane/values.yaml deleted file mode 100644 index b43d67a35..000000000 --- a/charts/edc-controlplane/values.yaml +++ /dev/null @@ -1,379 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -# Default values for edc-controlplane. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -# -- Specifies how many replicas of a deployed pod shall be created during the deployment -# Note: If horizontal pod autoscaling is enabled this setting has no effect -replicaCount: 1 - -image: - # -- Which derivate of the edc control-plane to use. - # One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql, ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-memory] - repository: ghcr.io/eclipse-tractusx/tractusx-edc/edc-controlplane-postgresql-hashicorp-vault - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecret: - # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) - # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). - # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. - dockerconfigjson: "" - -# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) -imagePullSecrets: [] - -# -- Overrides the charts name -nameOverride: "" - -# -- Overrides the releases full name -fullnameOverride: "" - -# -- Additional custom Labels to add -customLabels: {} - -serviceAccount: - # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release - create: true - # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account - annotations: {} - # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template - name: "" - -# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod -automountServiceAccountToken: false - -# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) -podAnnotations: {} - -# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment -podSecurityContext: - seccompProfile: - # -- Restrict a Container's Syscalls with seccomp - type: RuntimeDefault - # -- Runs all processes within a pod with a special uid - runAsUser: 10001 - # -- Processes within a pod will belong to this guid - runAsGroup: 10001 - # -- The owner for volumes and any files created within volumes will belong to this guid - fsGroup: 10001 - -# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod -securityContext: - capabilities: - # -- Specifies which capabilities to drop to reduce syscall attack surface - drop: - - ALL - # -- Specifies which capabilities to add to issue specialized syscalls - add: [] - # -- Whether the root filesystem is mounted in read-only mode - readOnlyRootFilesystem: true - # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID - allowPrivilegeEscalation: false - # -- Requires the container to run without root privileges - runAsNonRoot: true - # -- The container's process will run with the specified uid - runAsUser: 10001 - -livenessProbe: - # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - -readinessProbe: - # -- Whether to enable kubernetes readiness-probes - enabled: true - -startupProbe: - # -- Whether to enable kubernetes startup-probes - enabled: true - # -- Minimum consecutive failures for the probe to be considered failed after having succeeded - failureThreshold: 12 - # -- Number of seconds after the container has started before liveness probes are initiated. - initialDelaySeconds: 10 - -# -- Additional volumeMounts to the controlplane main container -volumeMounts: [] - -# -- Additional volumes to the controlplane pod -volumes: [] - -## EDC endpoints exposed by the control-plane -edc: - endpoints: - ## Default api exposing health checks etc - default: - # -- The network port, which the "default" api is going to be exposed by the container, pod and service - port: "8080" - # -- The path mapping the "default" api is going to be exposed at - path: /api - ## Data management API - data: - # -- The network port, which the "data" management api is going to be exposed by the container, pod and service - port: "8181" - # -- The path mapping the "data" management api is going to be exposed at - path: /data - ## Validation API - validation: - # -- The network port, which the "validation" api is going to be exposed by the container, pod and service - port: "8182" - # -- The path mapping the "validation" api is going to be exposed at - path: /validation - ## Control API - control: - # -- The network port, which the "control" api is going to be exposed by the container, pod and service - port: "9999" - # -- The path mapping the "control" api is going to be exposed at - path: /api/controlplane/control - ## IDS endpoints - ids: - # -- The network port, which the "ids" multipart api is going to be exposed by the container, pod and service - port: "8282" - # -- The path mapping the "ids" multipart api is going to be exposed at - path: /api/v1/ids - ## Prometheus endpoint - metrics: - # -- The network port, which the prometheus metrics are going to be exposed by the container, pod and service - port: "9090" - # -- The path mapping the prometheus metrics are going to be exposed at - path: /metrics - -service: - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. - type: ClusterIP - -## Ingress declaration to expose the network service. -ingresses: - ## Public / Internet facing Ingress - - enabled: true - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-controlplane.local" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - ids - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - - ## Private / Intranet facing Ingress - - enabled: false - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-controlplane.intranet" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - data - - control - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - -# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod -resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - -# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. -nodeSelector: {} - -# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. -tolerations: [] - -# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. -affinity: {} - -# -- Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) -# Ex.: -# JAVA_TOOL_OPTIONS: > -# -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 -env: {} - -# -- [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from -envSecretName: - -logging: - # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) - properties: |- - .level=INFO - org.eclipse.edc.level=ALL - handlers=java.util.logging.ConsoleHandler - java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter - java.util.logging.ConsoleHandler.level=ALL - java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n - -opentelemetry: - # -- opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) - properties: |- - otel.javaagent.enabled=true - otel.javaagent.debug=false - -configuration: - # -- EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-edc/Connector) - properties: |- - # edc.api.auth.key= - # edc.atomikos.checkpoint.interval= - # edc.atomikos.directory= - # edc.atomikos.logging= - # edc.atomikos.threaded2pc= - # edc.atomikos.timeout= - # edc.aws.access.key= - # edc.aws.provision.retry.retries.max= - # edc.aws.provision.role.duration.session.max= - # edc.aws.secret.access.key= - # edc.blobstore.endpoint= - # edc.dataplane.token.validation.endpoint= - # edc.core.retry.backoff.max= - # edc.core.retry.backoff.min= - # edc.core.retry.retries.max= - # edc.core.system.health.check.liveness-period= - # edc.core.system.health.check.readiness-period= - # edc.core.system.health.check.startup-period= - # edc.core.system.health.check.threadpool-size= - # edc.dataplane.queue.capacity= - # edc.dataplane.wait= - # edc.dataplane.workers= - # edc.datasource.asset.name="default" - # edc.datasource.contractdefinition.name="default" - # edc.datasource.contractnegotiation.name="default" - # edc.datasource.policy.name="default" - # edc.datasource.transferprocess.name="default" - # edc.datasource.default.pool.maxIdleConnections= - # edc.datasource.default.pool.maxTotalConnections= - # edc.datasource.default.pool.minIdleConnections= - # edc.datasource.default.pool.testConnectionOnBorrow= - # edc.datasource.default.pool.testConnectionOnCreate= - # edc.datasource.default.pool.testConnectionOnReturn= - # edc.datasource.default.pool.testConnectionWhileIdle= - # edc.datasource.default.pool.testQuery= - # edc.datasource.default.url= - # edc.datasource.default.user= - # edc.datasource.default.password= - # edc.dpf.selector.url= - # edc.events.topic.endpoint= - # edc.events.topic.name= - # edc.fs.config= - # edc.hostname= - # edc.identity.did.url= - # edc.ids.catalog.id= - # edc.ids.curator= - # edc.ids.description= - # edc.ids.endpoint= - # edc.ids.id= - # edc.ids.maintainer= - # edc.ids.security.profile= - # edc.ids.title= - # edc.ids.validation.referringconnector= - # edc.ion.crawler.did-type= - # edc.ion.crawler.interval-minutes= - # edc.ion.crawler.ion.url= - # edc.metrics.enabled= - # edc.metrics.executor.enabled= - # edc.metrics.jersey.enabled= - # edc.metrics.jetty.enabled= - # edc.metrics.okhttp.enabled= - # edc.metrics.system.enabled= - # edc.negotiation.consumer.state-machine.batch-size= - # edc.negotiation.provider.state-machine.batch-size= - # edc.oauth.client.id= - # edc.oauth.private.key.alias= - # edc.oauth.provider.audience= - # edc.oauth.provider.jwks.refresh= - # edc.oauth.provider.jwks.url= - # edc.oauth.public.key.alias= - # edc.oauth.token.url= - # edc.oauth.validation.nbf.leeway= - # edc.receiver.http.auth-code= - # edc.receiver.http.auth-key= - # edc.receiver.http.endpoint= - # edc.transfer.proxy.endpoint= - # edc.transfer.proxy.token.validity.seconds= - # edc.transfer.proxy.token.signer.privatekey.alias= - # edc.transfer.functions.check.endpoint= - # edc.transfer.functions.enabled.protocols= - # edc.transfer.functions.transfer.endpoint= - # edc.transfer-process-store.database.name= - # edc.transfer.state-machine.batch-size= - # edc.vault= - # edc.vault.certificate= - # edc.vault.clientid= - # edc.vault.clientsecret= - # edc.vault.name= - # edc.vault.tenantid= - # edc.vault.hashicorp.url= - # edc.vault.hashicorp.token= - # edc.vault.hashicorp.timeout.seconds= - # edc.webdid.doh.url= - # edc.web.rest.cors.enabled= - # edc.web.rest.cors.headers= - # edc.web.rest.cors.methods= - # edc.web.rest.cors.origins= - # ids.webhook.address= diff --git a/charts/edc-dataplane/Chart.yaml b/charts/edc-dataplane/Chart.yaml deleted file mode 100644 index 96d5598fa..000000000 --- a/charts/edc-dataplane/Chart.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v2 -name: edc-dataplane -description: >- - EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams -home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-dataplane -type: application -appVersion: "0.3.2" -version: 0.3.2 -deprecated: true -maintainers: [] -sources: - - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/edc-dataplane diff --git a/charts/edc-dataplane/LICENSE b/charts/edc-dataplane/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/charts/edc-dataplane/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/charts/edc-dataplane/README.md b/charts/edc-dataplane/README.md deleted file mode 100644 index 6085ccbbc..000000000 --- a/charts/edc-dataplane/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# edc-dataplane - -> **:exclamation: This Helm Chart is deprecated!** - -![Version: 0.3.2](https://img.shields.io/badge/Version-0.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.2](https://img.shields.io/badge/AppVersion-0.3.2-informational?style=flat-square) - -EDC Data-Plane - The Eclipse DataSpaceConnector data layer with responsibility of transferring and receiving data streams - -**Homepage:** - -## TL;DR - -```shell -helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-dataplane --version 0.3.2 -``` - -## Source Code - -* - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| affinity | object | `{}` | [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. | -| automountServiceAccountToken | bool | `false` | Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod | -| autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | -| autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| configuration.properties | string | `"# edc.atomikos.checkpoint.interval=\n# edc.atomikos.directory=\n# edc.atomikos.logging=\n# edc.atomikos.threaded2pc=\n# edc.atomikos.timeout=\n# edc.aws.access.key=\n# edc.aws.provision.retry.retries.max=\n# edc.aws.provision.role.duration.session.max=\n# edc.aws.secret.access.key=\n# edc.blobstore.endpoint=\n# edc.dataplane.token.validation.endpoint=\n# edc.core.retry.backoff.max=\n# edc.core.retry.backoff.min=\n# edc.core.retry.retries.max=\n# edc.core.system.health.check.liveness-period=\n# edc.core.system.health.check.readiness-period=\n# edc.core.system.health.check.startup-period=\n# edc.core.system.health.check.threadpool-size=\n# edc.dataplane.queue.capacity=\n# edc.dataplane.wait=\n# edc.dataplane.workers=\n# edc.datasource.asset.name=\"default\"\n# edc.datasource.contractdefinition.name=\"default\"\n# edc.datasource.contractnegotiation.name=\"default\"\n# edc.datasource.policy.name=\"default\"\n# edc.datasource.transferprocess.name=\"default\"\n# edc.datasource.default.pool.maxIdleConnections=\n# edc.datasource.default.pool.maxTotalConnections=\n# edc.datasource.default.pool.minIdleConnections=\n# edc.datasource.default.pool.testConnectionOnBorrow=\n# edc.datasource.default.pool.testConnectionOnCreate=\n# edc.datasource.default.pool.testConnectionOnReturn=\n# edc.datasource.default.pool.testConnectionWhileIdle=\n# edc.datasource.default.pool.testQuery=\n# edc.datasource.default.url=\n# edc.datasource.default.user=\n# edc.datasource.default.password=\n# edc.dpf.selector.url=\n# edc.events.topic.endpoint=\n# edc.events.topic.name=\n# edc.fs.config=\n# edc.hostname=\n# edc.identity.did.url=\n# edc.ids.catalog.id=\n# edc.ids.curator=\n# edc.ids.description=\n# edc.ids.endpoint=\n# edc.ids.endpoint.audience=\n# edc.ids.id=\n# edc.ids.maintainer=\n# edc.ids.security.profile=\n# edc.ids.title=\n# edc.ids.validation.referringconnector=\n# edc.ion.crawler.did-type=\n# edc.ion.crawler.interval-minutes=\n# edc.ion.crawler.ion.url=\n# edc.metrics.enabled=\n# edc.metrics.executor.enabled=\n# edc.metrics.jersey.enabled=\n# edc.metrics.jetty.enabled=\n# edc.metrics.okhttp.enabled=\n# edc.metrics.system.enabled=\n# edc.negotiation.consumer.state-machine.batch-size=\n# edc.negotiation.provider.state-machine.batch-size=\n# edc.oauth.client.id=\n# edc.oauth.private.key.alias=\n# edc.oauth.provider.jwks.refresh=\n# edc.oauth.provider.jwks.url=\n# edc.oauth.public.key.alias=\n# edc.oauth.token.url=\n# edc.oauth.validation.nbf.leeway=\n# edc.receiver.http.auth-code=\n# edc.receiver.http.auth-key=\n# edc.receiver.http.endpoint=\n# edc.transfer.functions.check.endpoint=\n# edc.transfer.functions.enabled.protocols=\n# edc.transfer.functions.transfer.endpoint=\n# edc.transfer-process-store.database.name=\n# edc.transfer.state-machine.batch-size=\n# edc.vault=\n# edc.vault.certificate=\n# edc.vault.clientid=\n# edc.vault.clientsecret=\n# edc.vault.name=\n# edc.vault.tenantid=\n# edc.vault.hashicorp.url=\n# edc.vault.hashicorp.token=\n# edc.vault.hashicorp.timeout.seconds=\n# edc.webdid.doh.url=\n# edc.web.rest.cors.enabled=\n# edc.web.rest.cors.headers=\n# edc.web.rest.cors.methods=\n# edc.web.rest.cors.origins="` | EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-edc/Connector) | -| customLabels | object | `{}` | Additional custom Labels to add | -| edc.endpoints.control.path | string | `"/api/dataplane/control"` | The path mapping the "control" api is going to be exposed by | -| edc.endpoints.control.port | string | `"9999"` | The network port, which the "control" api is going to be exposed by the container, pod and service | -| edc.endpoints.default.path | string | `"/api"` | The path mapping the "default" api is going to be exposed by | -| edc.endpoints.default.port | string | `"8080"` | The network port, which the "default" api is going to be exposed by the container, pod and service | -| edc.endpoints.metrics.path | string | `"/metrics"` | The path mapping the prometheus metrics are going to be exposed at | -| edc.endpoints.metrics.port | string | `"9090"` | The network port, which the prometheus metrics are going to be exposed by the container, pod and service | -| edc.endpoints.public.path | string | `"/api/public"` | The path mapping the "public" api is going to be exposed by | -| edc.endpoints.public.port | string | `"8185"` | The network port, which the "public" api is going to be exposed by the container, pod and service | -| env | object | `{}` | Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) Ex.: JAVA_TOOL_OPTIONS: > -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 | -| envSecretName | string | `nil` | [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from | -| fullnameOverride | string | `""` | Overrides the releases full name | -| image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| image.repository | string | `"ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault"` | Which derivate of the edc data-plane to use. One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-azure-vault] | -| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | -| imagePullSecret.dockerconfigjson | string | `""` | Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. | -| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | -| ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | -| ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| ingresses[0].enabled | bool | `true` | | -| ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | -| ingresses[0].hostname | string | `"edc-dataplane.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | -| livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| logging.properties | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) | -| nameOverride | string | `""` | Overrides the charts name | -| nodeSelector | object | `{}` | [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. | -| opentelemetry.properties | string | `"otel.javaagent.enabled=true\notel.javaagent.debug=false"` | opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) | -| podAnnotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) | -| podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | -| podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | -| podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | -| podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | -| readinessProbe.enabled | bool | `true` | Whether to enable kubernetes readiness-probes | -| replicaCount | int | `1` | Specifies how many replicas of a deployed pod shall be created during the deployment Note: If horizontal pod autoscaling is enabled this setting has no effect | -| resources | object | `{}` | [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod | -| securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | -| securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | -| securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | -| securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | -| securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | -| service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| serviceAccount.annotations | object | `{}` | [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account | -| serviceAccount.create | bool | `true` | Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release | -| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template | -| startupProbe.enabled | bool | `true` | Whether to enable kubernetes startup-probes | -| startupProbe.failureThreshold | int | `12` | Minimum consecutive failures for the probe to be considered failed after having succeeded | -| startupProbe.initialDelaySeconds | int | `10` | Number of seconds after the container has started before liveness probes are initiated. | -| tolerations | list | `[]` | [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. | - ----------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/edc-dataplane/README.md.gotmpl b/charts/edc-dataplane/README.md.gotmpl deleted file mode 100644 index c94d26d50..000000000 --- a/charts/edc-dataplane/README.md.gotmpl +++ /dev/null @@ -1,26 +0,0 @@ -{{ template "chart.header" . }} - -{{ template "chart.deprecationWarning" . }} - -{{ template "chart.badgesSection" . }} - -{{ template "chart.description" . }} - -{{ template "chart.homepageLine" . }} - -## TL;DR - -```shell -helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-dataplane --version {{ .Version }} -``` - -{{ template "chart.maintainersSection" . }} - -{{ template "chart.sourcesSection" . }} - -{{ template "chart.requirementsSection" . }} - -{{ template "chart.valuesSection" . }} - -{{ template "helm-docs.versionFooter" . }} diff --git a/charts/edc-dataplane/templates/NOTES.txt b/charts/edc-dataplane/templates/NOTES.txt deleted file mode 100644 index 454b250eb..000000000 --- a/charts/edc-dataplane/templates/NOTES.txt +++ /dev/null @@ -1,64 +0,0 @@ - -CHART NAME: {{ .Chart.Name }} -CHART VERSION: {{ .Chart.Version }} -APP VERSION: {{ .Chart.AppVersion }} - -Logs can be accessed by running this command: - - kubectl logs --tail 100 -f \ - --namespace {{ .Release.Namespace }} \ - -l "app.kubernetes.io/name={{ include "edc-dataplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" - -{{- if .Values.ingresses }} - -Following ingress URLS are available: - {{- $edcEndpoints := .Values.edc.endpoints }} - {{- range .Values.ingresses }} - {{- if .enabled }} - {{- $ingressEdcEndpoints := .endpoints }} - {{- $hostname := .hostname }} - {{- $tls := .tls }} - {{- range $name, $mapping := $edcEndpoints }} - {{- if (has $name $ingressEdcEndpoints) }} - Visit http{{ if $tls }}s{{ end }}://{{ $hostname }}{{ $mapping.path }} to access the {{ $name }} api - {{- end }} - {{- end }} - {{- end }} - {{- end }} - -{{- else if contains "NodePort" .Values.service.type }} -Get the application URLs by running these commands: - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - - export NODE_PORT_DEFAULT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") - export NODE_PORT_PUBLIC=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") - export NODE_PORT_CONTROL=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[2].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") - export NODE_PORT_METRICS=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[3].nodePort}" services {{ include "edc-dataplane.fullname" . }}}") - - echo "Visit http://$NODE_IP:$NODE_PORT_DEFAULT to access the default api" - echo "Visit http://$NODE_IP:$NODE_PORT_PUBLIC to access the public data transfer api" - echo "Visit http://$NODE_IP:$NODE_PORT_CONTROL to access the control api" - echo "Visit http://$NODE_IP:$NODE_PORT_METRICS to access the metrics api" - -{{- else if contains "ClusterIP" .Values.service.type }} -Get the application URL by running these commands: - - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "edc-dataplane.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - - export CONTAINER_PORT_DEFAULT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - export CONTAINER_PORT_PUBLIC=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[1].containerPort}") - export CONTAINER_PORT_CONTROL=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[2].containerPort}") - export CONTAINER_PORT_METRICS=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[3].containerPort}") - - echo "Visit http://127.0.0.1:8080 to access the default api" - echo "Visit http://127.0.0.1:8185 to access the public data transfer api" - echo "Visit http://127.0.0.1:9999 to access the control api" - echo "Visit http://127.0.0.1:9090 to access the metrics api" - - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME \ - 8080:$CONTAINER_PORT_DEFAULT \ - 8185:$CONTAINER_PORT_PUBLIC \ - 9999:$CONTAINER_PORT_CONTROL \ - 9090:$CONTAINER_PORT_METRICS - -{{- end }} diff --git a/charts/edc-dataplane/templates/configmap.yaml b/charts/edc-dataplane/templates/configmap.yaml deleted file mode 100644 index c7daa322f..000000000 --- a/charts/edc-dataplane/templates/configmap.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "edc-dataplane.fullname" . }}-configmap - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -data: - configuration.properties: |- - web.http.default.port={{ .Values.edc.endpoints.default.port }} - web.http.default.path={{ .Values.edc.endpoints.default.path }} - web.http.public.port={{ .Values.edc.endpoints.public.port }} - web.http.public.path={{ .Values.edc.endpoints.public.path }} - web.http.control.port={{ .Values.edc.endpoints.control.port }} - web.http.control.path={{ .Values.edc.endpoints.control.path }} - {{- .Values.configuration.properties | nindent 4 }} - - opentelemetry.properties: |- - {{- .Values.opentelemetry.properties | nindent 4 }} - - logging.properties: |- - {{- .Values.logging.properties | nindent 4 }} diff --git a/charts/edc-dataplane/templates/deployment.yaml b/charts/edc-dataplane/templates/deployment.yaml deleted file mode 100644 index 474b04650..000000000 --- a/charts/edc-dataplane/templates/deployment.yaml +++ /dev/null @@ -1,142 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "edc-dataplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "edc-dataplane.selectorLabels" . | nindent 6 }} - template: - metadata: - annotations: - checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - checksum/env-config: {{ include (print $.Template.BasePath "/configmap-env.yaml") . | sha256sum }} - {{- with .Values.podAnnotations }} - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "edc-dataplane.selectorLabels" . | nindent 8 }} - spec: - {{- if .Values.imagePullSecret.dockerconfigjson }} - imagePullSecrets: - - name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret - {{- else }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- end }} - serviceAccountName: {{ include "edc-dataplane.serviceAccountName" . }} - automountServiceAccountToken: {{ if .Values.automountServiceAccountToken }}true{{ else }}false{{ end }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: default - containerPort: {{ .Values.edc.endpoints.default.port }} - protocol: TCP - - name: public - containerPort: {{ .Values.edc.endpoints.public.port }} - protocol: TCP - - name: control - containerPort: {{ .Values.edc.endpoints.control.port }} - protocol: TCP - - name: metrics - containerPort: {{ .Values.edc.endpoints.metrics.port }} - protocol: TCP - {{- if .Values.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/liveness - port: default - {{- end }} - {{- if .Values.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/readiness - port: default - {{- end }} - {{- if .Values.startupProbe.enabled }} - startupProbe: - httpGet: - path: {{ .Values.edc.endpoints.default.path }}/check/startup - port: default - failureThreshold: {{ .Values.startupProbe.failureThreshold }} - initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} - {{- end }} - envFrom: - - configMapRef: - name: {{ include "edc-dataplane.fullname" . }}-env - {{- if .Values.envSecretName }} - - secretRef: - name: {{ .Values.envSecretName | quote }} - {{- end }} - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: configuration - mountPath: /app/configuration.properties - subPath: configuration.properties - - name: configuration - mountPath: /app/opentelemetry.properties - subPath: opentelemetry.properties - - name: configuration - mountPath: /app/logging.properties - subPath: logging.properties - volumes: - - name: configuration - configMap: - name: {{ include "edc-dataplane.fullname" . }}-configmap - items: - - key: configuration.properties - path: configuration.properties - - key: opentelemetry.properties - path: opentelemetry.properties - - key: logging.properties - path: logging.properties - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/charts/edc-dataplane/templates/hpa.yaml b/charts/edc-dataplane/templates/hpa.yaml deleted file mode 100644 index 037934aeb..000000000 --- a/charts/edc-dataplane/templates/hpa.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.autoscaling.enabled }} ---- -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "edc-dataplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "edc-dataplane.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/charts/edc-dataplane/templates/imagepullsecret.yaml b/charts/edc-dataplane/templates/imagepullsecret.yaml deleted file mode 100644 index 11961674b..000000000 --- a/charts/edc-dataplane/templates/imagepullsecret.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.imagePullSecret.dockerconfigjson }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -data: - .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} -type: kubernetes.io/dockerconfigjson -{{- end }} diff --git a/charts/edc-dataplane/templates/service.yaml b/charts/edc-dataplane/templates/service.yaml deleted file mode 100644 index e4d081776..000000000 --- a/charts/edc-dataplane/templates/service.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "edc-dataplane.fullname" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.edc.endpoints.default.port }} - targetPort: default - protocol: TCP - name: default - - port: {{ .Values.edc.endpoints.control.port }} - targetPort: control - protocol: TCP - name: control - - port: {{ .Values.edc.endpoints.public.port }} - targetPort: public - protocol: TCP - name: public - - port: {{ .Values.edc.endpoints.metrics.port }} - targetPort: metrics - protocol: TCP - name: metrics - selector: - {{- include "edc-dataplane.selectorLabels" . | nindent 4 }} diff --git a/charts/edc-dataplane/templates/serviceaccount.yaml b/charts/edc-dataplane/templates/serviceaccount.yaml deleted file mode 100644 index 39a44d35e..000000000 --- a/charts/edc-dataplane/templates/serviceaccount.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- if .Values.serviceAccount.create -}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "edc-dataplane.serviceAccountName" . }} - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-dataplane.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/charts/edc-dataplane/values.yaml b/charts/edc-dataplane/values.yaml deleted file mode 100644 index 9a049cb1f..000000000 --- a/charts/edc-dataplane/values.yaml +++ /dev/null @@ -1,331 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -# Default values for edc-dataplane. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -# -- Specifies how many replicas of a deployed pod shall be created during the deployment -# Note: If horizontal pod autoscaling is enabled this setting has no effect -replicaCount: 1 - -image: - # -- Which derivate of the edc data-plane to use. - # One of: [ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault, ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-azure-vault] - repository: ghcr.io/eclipse-tractusx/tractusx-edc/edc-dataplane-hashicorp-vault - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion - tag: "" - -imagePullSecret: - # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) - # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). - # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. - dockerconfigjson: "" - -# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) -imagePullSecrets: [] - -# -- Overrides the charts name -nameOverride: "" - -# -- Overrides the releases full name -fullnameOverride: "" - -# -- Additional custom Labels to add -customLabels: {} - -serviceAccount: - # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release - create: true - # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account - annotations: {} - # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template - name: "" - -# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod -automountServiceAccountToken: false - -# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) -podAnnotations: {} - -# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment -podSecurityContext: - seccompProfile: - # -- Restrict a Container's Syscalls with seccomp - type: RuntimeDefault - # -- Runs all processes within a pod with a special uid - runAsUser: 10001 - # -- Processes within a pod will belong to this guid - runAsGroup: 10001 - # -- The owner for volumes and any files created within volumes will belong to this guid - fsGroup: 10001 - -# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod -securityContext: - capabilities: - # -- Specifies which capabilities to drop to reduce syscall attack surface - drop: - - ALL - # -- Specifies which capabilities to add to issue specialized syscalls - add: [] - # -- Whether the root filesystem is mounted in read-only mode - readOnlyRootFilesystem: true - # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID - allowPrivilegeEscalation: false - # -- Requires the container to run without root privileges - runAsNonRoot: true - # -- The container's process will run with the specified uid - runAsUser: 10001 - -livenessProbe: - # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - -readinessProbe: - # -- Whether to enable kubernetes readiness-probes - enabled: true - -startupProbe: - # -- Whether to enable kubernetes startup-probes - enabled: true - # -- Minimum consecutive failures for the probe to be considered failed after having succeeded - failureThreshold: 12 - # -- Number of seconds after the container has started before liveness probes are initiated. - initialDelaySeconds: 10 - -## EDC endpoints exposed by the data-plane -edc: - endpoints: - ## Default api exposing health checks etc - default: - # -- The network port, which the "default" api is going to be exposed by the container, pod and service - port: "8080" - # -- The path mapping the "default" api is going to be exposed by - path: /api - ## Public endpoint for data transfer - public: - # -- The network port, which the "public" api is going to be exposed by the container, pod and service - port: "8185" - # -- The path mapping the "public" api is going to be exposed by - path: /api/public - ## Control API - control: - # -- The network port, which the "control" api is going to be exposed by the container, pod and service - port: "9999" - # -- The path mapping the "control" api is going to be exposed by - path: /api/dataplane/control - ## Prometheus endpoint - metrics: - # -- The network port, which the prometheus metrics are going to be exposed by the container, pod and service - port: "9090" - # -- The path mapping the prometheus metrics are going to be exposed at - path: /metrics - -service: - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. - type: ClusterIP - -## Ingress declaration to expose the network service. -ingresses: - ## Public / Internet facing Ingress - - enabled: true - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-dataplane.local" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - public - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - -# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod -resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - -# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. -nodeSelector: {} - -# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. -tolerations: [] - -# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. -affinity: {} - -# -- Container environment variables e.g. for configuring [JAVA_TOOL_OPTIONS](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html) -# Ex.: -# JAVA_TOOL_OPTIONS: > -# -Dhttp.proxyHost=proxy -Dhttp.proxyPort=80 -Dhttp.nonProxyHosts="localhost|127.*|[::1]" -Dhttps.proxyHost=proxy -Dhttps.proxyPort=443 -env: {} - -# -- [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) name to load environment variables from -envSecretName: - -logging: - # -- EDC logging.properties configuring the [java.util.logging subsystem](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html#a1.8) - properties: |- - .level=INFO - org.eclipse.edc.level=ALL - handlers=java.util.logging.ConsoleHandler - java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter - java.util.logging.ConsoleHandler.level=ALL - java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n - -opentelemetry: - # -- opentelemetry.properties configuring the [opentelemetry agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) - properties: |- - otel.javaagent.enabled=true - otel.javaagent.debug=false - -configuration: - # -- EDC configuration.properties configuring aspects of the [eclipse-dataspaceconnector](https://github.com/eclipse-edc/Connector) - properties: |- - # edc.atomikos.checkpoint.interval= - # edc.atomikos.directory= - # edc.atomikos.logging= - # edc.atomikos.threaded2pc= - # edc.atomikos.timeout= - # edc.aws.access.key= - # edc.aws.provision.retry.retries.max= - # edc.aws.provision.role.duration.session.max= - # edc.aws.secret.access.key= - # edc.blobstore.endpoint= - # edc.dataplane.token.validation.endpoint= - # edc.core.retry.backoff.max= - # edc.core.retry.backoff.min= - # edc.core.retry.retries.max= - # edc.core.system.health.check.liveness-period= - # edc.core.system.health.check.readiness-period= - # edc.core.system.health.check.startup-period= - # edc.core.system.health.check.threadpool-size= - # edc.dataplane.queue.capacity= - # edc.dataplane.wait= - # edc.dataplane.workers= - # edc.datasource.asset.name="default" - # edc.datasource.contractdefinition.name="default" - # edc.datasource.contractnegotiation.name="default" - # edc.datasource.policy.name="default" - # edc.datasource.transferprocess.name="default" - # edc.datasource.default.pool.maxIdleConnections= - # edc.datasource.default.pool.maxTotalConnections= - # edc.datasource.default.pool.minIdleConnections= - # edc.datasource.default.pool.testConnectionOnBorrow= - # edc.datasource.default.pool.testConnectionOnCreate= - # edc.datasource.default.pool.testConnectionOnReturn= - # edc.datasource.default.pool.testConnectionWhileIdle= - # edc.datasource.default.pool.testQuery= - # edc.datasource.default.url= - # edc.datasource.default.user= - # edc.datasource.default.password= - # edc.dpf.selector.url= - # edc.events.topic.endpoint= - # edc.events.topic.name= - # edc.fs.config= - # edc.hostname= - # edc.identity.did.url= - # edc.ids.catalog.id= - # edc.ids.curator= - # edc.ids.description= - # edc.ids.endpoint= - # edc.ids.endpoint.audience= - # edc.ids.id= - # edc.ids.maintainer= - # edc.ids.security.profile= - # edc.ids.title= - # edc.ids.validation.referringconnector= - # edc.ion.crawler.did-type= - # edc.ion.crawler.interval-minutes= - # edc.ion.crawler.ion.url= - # edc.metrics.enabled= - # edc.metrics.executor.enabled= - # edc.metrics.jersey.enabled= - # edc.metrics.jetty.enabled= - # edc.metrics.okhttp.enabled= - # edc.metrics.system.enabled= - # edc.negotiation.consumer.state-machine.batch-size= - # edc.negotiation.provider.state-machine.batch-size= - # edc.oauth.client.id= - # edc.oauth.private.key.alias= - # edc.oauth.provider.jwks.refresh= - # edc.oauth.provider.jwks.url= - # edc.oauth.public.key.alias= - # edc.oauth.token.url= - # edc.oauth.validation.nbf.leeway= - # edc.receiver.http.auth-code= - # edc.receiver.http.auth-key= - # edc.receiver.http.endpoint= - # edc.transfer.functions.check.endpoint= - # edc.transfer.functions.enabled.protocols= - # edc.transfer.functions.transfer.endpoint= - # edc.transfer-process-store.database.name= - # edc.transfer.state-machine.batch-size= - # edc.vault= - # edc.vault.certificate= - # edc.vault.clientid= - # edc.vault.clientsecret= - # edc.vault.name= - # edc.vault.tenantid= - # edc.vault.hashicorp.url= - # edc.vault.hashicorp.token= - # edc.vault.hashicorp.timeout.seconds= - # edc.webdid.doh.url= - # edc.web.rest.cors.enabled= - # edc.web.rest.cors.headers= - # edc.web.rest.cors.methods= - # edc.web.rest.cors.origins= diff --git a/charts/edc-controlplane/.helmignore b/charts/tractusx-connector-memory/.helmignore similarity index 82% rename from charts/edc-controlplane/.helmignore rename to charts/tractusx-connector-memory/.helmignore index 148b31d6c..0e8a0eb36 100644 --- a/charts/edc-controlplane/.helmignore +++ b/charts/tractusx-connector-memory/.helmignore @@ -21,9 +21,3 @@ .idea/ *.tmproj .vscode/ - -README.md.gotmpl - -# Accept only values.yaml -values?*.yaml -values?*.yml diff --git a/charts/tractusx-connector-memory/Chart.yaml b/charts/tractusx-connector-memory/Chart.yaml new file mode 100644 index 000000000..cb0a06b72 --- /dev/null +++ b/charts/tractusx-connector-memory/Chart.yaml @@ -0,0 +1,45 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +apiVersion: v2 +name: tractusx-connector-memory +description: A Helm chart for Tractus-X Eclipse Data Space Connector based on memory +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.3.3 +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.3.3" +home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory +sources: + - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md new file mode 100644 index 000000000..798342502 --- /dev/null +++ b/charts/tractusx-connector-memory/README.md @@ -0,0 +1,147 @@ +# tractusx-connector-memory + +![Version: 0.3.3](https://img.shields.io/badge/Version-0.3.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.3](https://img.shields.io/badge/AppVersion-0.3.3-informational?style=flat-square) + +A Helm chart for Tractus-X Eclipse Data Space Connector based on memory + +**Homepage:** + +## TL;DR + +```shell +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 +``` + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| backendService.httpProxyTokenReceiverUrl | string | `""` | | +| customLabels | object | `{}` | | +| daps.clientId | string | `""` | | +| daps.paths.jwks | string | `"/jwks.json"` | | +| daps.paths.token | string | `"/token"` | | +| daps.url | string | `""` | | +| fullnameOverride | string | `""` | | +| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| nameOverride | string | `""` | | +| runtime.affinity | object | `{}` | | +| runtime.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | +| runtime.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| runtime.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| runtime.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| runtime.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| runtime.businessPartnerValidation.log.agreementValidation | bool | `true` | | +| runtime.debug.enabled | bool | `false` | | +| runtime.debug.port | int | `1044` | | +| runtime.debug.suspendOnStart | bool | `false` | | +| runtime.endpoints | object | `{"control":{"path":"/control","port":8083},"data":{"authKey":"","path":"/data","port":8081},"default":{"path":"/api","port":8080},"ids":{"path":"/api/v1/ids","port":8084},"observability":{"insecure":true,"path":"/observability","port":8085},"public":{"path":"/api/public","port":8086},"validation":{"path":"/validation","port":8082}}` | endpoints of the control plane | +| runtime.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | +| runtime.endpoints.control.path | string | `"/control"` | path for incoming api calls | +| runtime.endpoints.control.port | int | `8083` | port for incoming api calls | +| runtime.endpoints.data | object | `{"authKey":"","path":"/data","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | +| runtime.endpoints.data.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | +| runtime.endpoints.data.path | string | `"/data"` | path for incoming api calls | +| runtime.endpoints.data.port | int | `8081` | port for incoming api calls | +| runtime.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | +| runtime.endpoints.default.path | string | `"/api"` | path for incoming api calls | +| runtime.endpoints.default.port | int | `8080` | port for incoming api calls | +| runtime.endpoints.ids | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | +| runtime.endpoints.ids.path | string | `"/api/v1/ids"` | path for incoming api calls | +| runtime.endpoints.ids.port | int | `8084` | port for incoming api calls | +| runtime.endpoints.observability | object | `{"insecure":true,"path":"/observability","port":8085}` | observability api with unsecured access, must not be internet facing | +| runtime.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | +| runtime.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | +| runtime.endpoints.observability.port | int | `8085` | port for incoming API calls | +| runtime.endpoints.validation | object | `{"path":"/validation","port":8082}` | validation api, only used by the data plane and should not be added to any ingress | +| runtime.endpoints.validation.path | string | `"/validation"` | path for incoming api calls | +| runtime.endpoints.validation.port | int | `8082` | port for incoming api calls | +| runtime.env | object | `{}` | | +| runtime.envConfigMapNames | list | `[]` | | +| runtime.envSecretNames | list | `[]` | | +| runtime.envValueFrom | object | `{}` | | +| runtime.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| runtime.image.repository | string | `""` | | +| runtime.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | +| runtime.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| runtime.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| runtime.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| runtime.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| runtime.ingresses[0].enabled | bool | `false` | | +| runtime.ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | +| runtime.ingresses[0].hostname | string | `"edc-control.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| runtime.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| runtime.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| runtime.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | +| runtime.ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | +| runtime.ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| runtime.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| runtime.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| runtime.ingresses[1].enabled | bool | `false` | | +| runtime.ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | +| runtime.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| runtime.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| runtime.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| runtime.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | +| runtime.initContainers | list | `[]` | | +| runtime.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | +| runtime.internationalDataSpaces.curator | string | `""` | | +| runtime.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | +| runtime.internationalDataSpaces.id | string | `"TXDC"` | | +| runtime.internationalDataSpaces.maintainer | string | `""` | | +| runtime.internationalDataSpaces.title | string | `""` | | +| runtime.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| runtime.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| runtime.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | +| runtime.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| runtime.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| runtime.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| runtime.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| runtime.nodeSelector | object | `{}` | | +| runtime.podAnnotations | object | `{}` | additional annotations for the pod | +| runtime.podLabels | object | `{}` | additional labels for the pod | +| runtime.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| runtime.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| runtime.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| runtime.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| runtime.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | +| runtime.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| runtime.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| runtime.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | +| runtime.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a readiness check every 10 seconds | +| runtime.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| runtime.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| runtime.replicaCount | int | `1` | | +| runtime.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | +| runtime.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | +| runtime.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| runtime.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| runtime.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | +| runtime.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| runtime.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | +| runtime.service.annotations | object | `{}` | | +| runtime.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| runtime.tolerations | list | `[]` | | +| runtime.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | +| runtime.url.public | string | `""` | | +| runtime.url.readiness | string | `""` | | +| runtime.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | +| runtime.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.imagePullSecrets | list | `[]` | Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| serviceAccount.name | string | `""` | | +| vault.secretNames.dapsPrivateKey | string | `"daps-private-key"` | | +| vault.secretNames.dapsPublicKey | string | `"daps-public-key"` | | +| vault.secretNames.transferProxyTokenEncryptionAesKey | string | `"transfer-proxy-token-encryption-aes-key"` | | +| vault.secretNames.transferProxyTokenSignerPrivateKey | string | `"transfer-proxy-token-signer-private-key"` | | +| vault.secretNames.transferProxyTokenSignerPublicKey | string | `"transfer-proxy-token-signer-public-key"` | | +| vault.secrets | string | `""` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/edc-controlplane/README.md.gotmpl b/charts/tractusx-connector-memory/README.md.gotmpl similarity index 86% rename from charts/edc-controlplane/README.md.gotmpl rename to charts/tractusx-connector-memory/README.md.gotmpl index aa70ec6fc..b1671f5a2 100644 --- a/charts/edc-controlplane/README.md.gotmpl +++ b/charts/tractusx-connector-memory/README.md.gotmpl @@ -12,7 +12,7 @@ ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/edc-controlplane --version {{ .Version }} +helm install my-release tractusx-edc/tractusx-connector --version {{ .Version }} ``` {{ template "chart.maintainersSection" . }} diff --git a/charts/tractusx-connector-memory/example.yaml b/charts/tractusx-connector-memory/example.yaml new file mode 100644 index 000000000..57d12b039 --- /dev/null +++ b/charts/tractusx-connector-memory/example.yaml @@ -0,0 +1,65 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +## This file can be used to verify that the chart is working properly. It provides an exemplary configuration +## that is intended to be used with the supporting infrastructure. +## 1. install DAPS: +## helm install infrastructure edc-tests/deployment/src/main/resources/helm/test-infrastructure \ ─╯ +## --wait-for-jobs +## +## 2. install in-mem runtime. Note that the key and crt must match exactly the DAPS setup, c.f. edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml +## export DAPSKEY="" +## export DAPSCRT="" +## export YOUR_VAULT_SECRETS="daps-key:$DAPSKEY;daps-crt:$DAPSCRT" +## helm install trudy charts/tractusx-connector-memory -f charts/tractusx-connector-memory/example.yaml --set vault.secrets=$YOUR_VAULT_SECRETS + +fullnameOverride: tx-inmem +runtime: + service: + type: NodePort + endpoints: + data: + authKey: password + image: + pullPolicy: Never + tag: "latest" + repository: "edc-runtime-memory" + securityContext: + # avoids some errors in the log: cannot write temp files of large multipart requests when R/O + readOnlyRootFilesystem: false + +vault: + secretNames: + transferProxyTokenSignerPublicKey: daps-crt + transferProxyTokenSignerPrivateKey: daps-key + transferProxyTokenEncryptionAesKey: aes-keysc + dapsPrivateKey: daps-key + dapsPublicKey: daps-crt + + # this must be set through CLI args: --set vault.secrets=$YOUR_VAULT_SECRETS where YOUR_VAULT_SECRETS should + # be a string in the format "key1:secret1;key2:secret2;..." + secrets: + +daps: + url: "http://ids-daps:4567" + clientId: "E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65" + +backendService: + httpProxyTokenReceiverUrl: "http://backend:8080" diff --git a/charts/tractusx-connector-memory/templates/NOTES.txt b/charts/tractusx-connector-memory/templates/NOTES.txt new file mode 100644 index 000000000..cd49a4d15 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the runtime URL by running these commands: +{{ with index .Values.runtime.ingresses 0}} +{{- if .enabled }} +{{- range .paths }} + http{{ if .tls }}s{{ end }}://{{ .hostname }}{{ .path }} +{{- end }} +{{- else if contains "NodePort" $.Values.runtime.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ $.Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "txdc.fullname" $ }}) + export NODE_IP=$(kubectl get nodes --namespace {{ $.Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" $.Values.runtime.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "txdc.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "txdc.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ $.Values.runtime.service.port }} +{{- else if contains "ClusterIP" $.Values.runtime.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ $.Release.Namespace }} -l "app.kubernetes.io/name={{ include "txdc.name" $ }},app.kubernetes.io/instance={{ $.Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ $.Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ $.Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector-memory/templates/_helpers.tpl b/charts/tractusx-connector-memory/templates/_helpers.tpl new file mode 100644 index 000000000..1b70bf13b --- /dev/null +++ b/charts/tractusx-connector-memory/templates/_helpers.tpl @@ -0,0 +1,157 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "txdc.name" -}} +{{- default .Chart.Name .Values.nameOverride | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "txdc.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "txdc.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Control Common labels +*/}} +{{- define "txdc.labels" -}} +helm.sh/chart: {{ include "txdc.chart" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Control Common labels +*/}} +{{- define "txdc.runtime.labels" -}} +helm.sh/chart: {{ include "txdc.chart" . }} +{{ include "txdc.runtime.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: edc-runtime +app.kubernetes.io/part-of: edc +{{- end }} + +{{/* +Control Selector labels +*/}} +{{- define "txdc.runtime.selectorLabels" -}} +app.kubernetes.io/name: {{ include "txdc.name" . }}-runtime +app.kubernetes.io/instance: {{ .Release.Name }}-runtime +{{- end }} + +{{/* +Data Selector labels +*/}} +{{- define "txdc.dataplane.selectorLabels" -}} +app.kubernetes.io/name: {{ include "txdc.name" . }}-dataplane +app.kubernetes.io/instance: {{ .Release.Name }}-dataplane +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "txdc.runtime.serviceaccount.name" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "txdc.fullname" . ) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Control IDS URL +*/}} +{{- define "txdc.runtime.url.ids" -}} +{{- if .Values.runtime.url.ids }}{{/* if ids api url has been specified explicitly */}} +{{- .Values.runtime.url.ids }} +{{- else }}{{/* else when ids api url has not been specified explicitly */}} +{{- with (index .Values.runtime.ingresses 0) }} +{{- if .enabled }}{{/* if ingress enabled */}} +{{- if .tls.enabled }}{{/* if TLS enabled */}} +{{- printf "https://%s" .hostname -}} +{{- else }}{{/* else when TLS not enabled */}} +{{- printf "http://%s" .hostname -}} +{{- end }}{{/* end if tls */}} +{{- else }}{{/* else when ingress not enabled */}} +{{- printf "http://%s-runtime:%v" ( include "txdc.fullname" $ ) $.Values.runtime.endpoints.ids.port -}} +{{- end }}{{/* end if ingress */}} +{{- end }}{{/* end with ingress */}} +{{- end }}{{/* end if .Values.runtime.url.ids */}} +{{- end }} + +{{/* +Observability URL +*/}} +{{- define "tdxc.runtime.url.readiness" -}} +{{- printf "http://%s-runtime:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.observability.port $.Values.runtime.endpoints.observability.path -}} +{{- end }} + +{{/* +Validation URL +*/}} +{{- define "txdc.runtime.url.validation" -}} +{{- printf "http://%s-runtime:%v%s/token" ( include "txdc.fullname" $ ) $.Values.runtime.endpoints.validation.port $.Values.runtime.endpoints.validation.path -}} +{{- end }} + +{{/* +Data Control URL +*/}} +{{- define "txdc.dataplane.url.control" -}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" . ) .Values.runtime.endpoints.control.port .Values.runtime.endpoints.control.path -}} +{{- end }} + +{{/* +Data Public URL +*/}} +{{- define "txdc.dataplane.url.public" -}} +{{- if .Values.runtime.url.public }}{{/* if public api url has been specified explicitly */}} +{{- .Values.runtime.url.public }} +{{- else }}{{/* else when public api url has not been specified explicitly */}} +{{- with (index .Values.runtime.ingresses 0) }} +{{- if .enabled }}{{/* if ingress enabled */}} +{{- if .tls.enabled }}{{/* if TLS enabled */}} +{{- printf "https://%s%s" .hostname $.Values.runtime.endpoints.public.path -}} +{{- else }}{{/* else when TLS not enabled */}} +{{- printf "http://%s%s" .hostname $.Values.runtime.endpoints.public.path -}} +{{- end }}{{/* end if tls */}} +{{- else }}{{/* else when ingress not enabled */}} +{{- printf "http://%s-dataplane:%v%s" (include "txdc.fullname" $ ) $.Values.runtime.endpoints.public.port $.Values.runtime.endpoints.public.path -}} +{{- end }}{{/* end if ingress */}} +{{- end }}{{/* end with ingress */}} +{{- end }}{{/* end if .Values.dataplane.url.public */}} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "txdc.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "txdc.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector-memory/templates/configmap-runtime.yaml b/charts/tractusx-connector-memory/templates/configmap-runtime.yaml new file mode 100644 index 000000000..8b6067e06 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/configmap-runtime.yaml @@ -0,0 +1,33 @@ +# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # 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 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "txdc.fullname" . }}-runtime + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} +data: + logging.properties: |- + {{- .Values.runtime.logging | nindent 4 }} diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml new file mode 100644 index 000000000..3e7bd89e3 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -0,0 +1,308 @@ +# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # 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 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "txdc.fullname" . }} + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} +spec: + {{- if not .Values.runtime.autoscaling.enabled }} + replicas: {{ .Values.runtime.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "txdc.runtime.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.runtime.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "txdc.runtime.selectorLabels" . | nindent 8 }} + {{- with .Values.runtime.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "txdc.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.runtime.podSecurityContext | nindent 8 }} + initContainers: + {{- toYaml .Values.runtime.initContainers | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.runtime.securityContext | nindent 12 }} + # either use the specified image, or use the default one + {{- if .Values.runtime.image.repository }} + image: "{{ .Values.runtime.image.repository }}:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" + {{- else }} + image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-runtime-memory:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" + {{- end }} + + imagePullPolicy: {{ .Values.runtime.image.pullPolicy }} + ports: + {{- range $key,$value := .Values.runtime.endpoints }} + - name: {{ $key }} + containerPort: {{ $value.port }} + protocol: TCP + {{- end }} + {{- if .Values.runtime.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.runtime.endpoints.observability.path }}/check/liveness + port: {{ .Values.runtime.endpoints.observability.port }} + initialDelaySeconds: {{ .Values.runtime.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.runtime.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.runtime.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.runtime.livenessProbe.failureThreshold }} + successThreshold: {{ .Values.runtime.livenessProbe.successThreshold }} + {{- end }} + {{- if .Values.runtime.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.runtime.endpoints.observability.path }}/check/readiness + port: {{ .Values.runtime.endpoints.observability.port }} + initialDelaySeconds: {{ .Values.runtime.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.runtime.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.runtime.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.runtime.readinessProbe.failureThreshold }} + successThreshold: {{ .Values.runtime.readinessProbe.successThreshold }} + {{- end }} + resources: + {{- toYaml .Values.runtime.resources | nindent 12 }} + env: + {{- if .Values.runtime.debug.enabled }} + - name: "JAVA_TOOL_OPTIONS" + {{- if and .Values.runtime.debug.enabled .Values.runtime.debug.suspendOnStart }} + value: >- + {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=%v" .Values.runtime.debug.port }} + {{- else }} + value: >- + {{ printf "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=%v" .Values.runtime.debug.port }} + {{- end }} + {{- end }} + + ######################## + ## DAPS CONFIGURATION ## + ######################## + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/iam/oauth2/oauth2-core + - name: EDC_OAUTH_CLIENT_ID + value: {{ .Values.daps.clientId | required ".Values.daps.clientId is required" | quote }} + - name: EDC_OAUTH_PROVIDER_JWKS_URL + value: {{ printf "%s%s" .Values.daps.url .Values.daps.paths.jwks }} + - name: EDC_OAUTH_TOKEN_URL + value: {{ printf "%s%s" .Values.daps.url .Values.daps.paths.token }} + - name: EDC_OAUTH_PRIVATE_KEY_ALIAS + value: {{ .Values.vault.secretNames.dapsPrivateKey | required ".Values.vault.secretNames.dapsPrivateKey is required" | quote }} + - name: EDC_OAUTH_PUBLIC_KEY_ALIAS + value: {{ .Values.vault.secretNames.dapsPublicKey | required ".Values.vault.secretNames.dapsPublicKey is required" | quote }} + + ####### + # API # + ####### + - name: "EDC_API_AUTH_KEY" + value: {{ .Values.runtime.endpoints.data.authKey | required ".Values.runtime.endpoints.data.authKey is required" | quote }} + - name: "WEB_HTTP_DEFAULT_PORT" + value: {{ .Values.runtime.endpoints.default.port | quote }} + - name: "WEB_HTTP_DEFAULT_PATH" + value: {{ .Values.runtime.endpoints.default.path | quote }} + {{- if or (eq (substr 0 3 .Values.runtime.image.tag) "0.1") (eq (substr 0 3 .Values.runtime.image.tag) "0.2") }} + # WEB_HTTP_DATA_PORT is renamed to WEB_HTTP_MANAGEMENT_PORT from version 0.2.1 and newer + # we will keep both settings for downward capabilities + - name: "WEB_HTTP_DATA_PORT" + value: {{ .Values.runtime.endpoints.data.port | quote }} + # WEB_HTTP_DATA_PATH is renamed to WEB_HTTP_MANAGEMENT_PATH from version 0.2.1 and newer + # we will keep both settings for downward capabilities + - name: "WEB_HTTP_DATA_PATH" + value: {{ .Values.runtime.endpoints.data.path | quote }} + {{- else }} + - name: "WEB_HTTP_MANAGEMENT_PORT" + value: {{ .Values.runtime.endpoints.data.port | quote }} + - name: "WEB_HTTP_MANAGEMENT_PATH" + value: {{ .Values.runtime.endpoints.data.path | quote }} + {{- end }} + - name: "WEB_HTTP_VALIDATION_PORT" + value: {{ .Values.runtime.endpoints.validation.port | quote }} + - name: "WEB_HTTP_VALIDATION_PATH" + value: {{ .Values.runtime.endpoints.validation.path | quote }} + - name: "WEB_HTTP_CONTROL_PORT" + value: {{ .Values.runtime.endpoints.control.port | quote }} + - name: "WEB_HTTP_CONTROL_PATH" + value: {{ .Values.runtime.endpoints.control.path | quote }} + - name: "WEB_HTTP_IDS_PORT" + value: {{ .Values.runtime.endpoints.ids.port | quote }} + - name: "WEB_HTTP_IDS_PATH" + value: {{ .Values.runtime.endpoints.ids.path | quote }} + - name: "WEB_HTTP_OBSERVABILITY_PORT" + value: {{ .Values.runtime.endpoints.observability.port | quote}} + - name: "WEB_HTTP_OBSERVABILITY_PATH" + value: {{ .Values.runtime.endpoints.observability.path | quote}} + - name: "TRACTUSX_API_OBSERVABILITY_ALLOW-INSECURE" + value: {{ .Values.runtime.endpoints.observability.insecure | quote }} + - name: "WEB_HTTP_PUBLIC_PORT" + value: {{ .Values.runtime.endpoints.public.port | quote }} + - name: "WEB_HTTP_PUBLIC_PATH" + value: {{ .Values.runtime.endpoints.public.path | quote }} + - name: "EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT" + value: {{ include "txdc.runtime.url.validation" .}} + + ######### + ## IDS ## + ######### + - name: "IDS_WEBHOOK_ADDRESS" + value: {{ include "txdc.runtime.url.ids" . | quote }} + - name: "EDC_IDS_ENDPOINT" + value: {{ printf "%s%s" (include "txdc.runtime.url.ids" .) .Values.runtime.endpoints.ids.path | quote }} + - name: "EDC_IDS_ID" + value: {{ printf "urn:connector:%s" (lower .Values.runtime.internationalDataSpaces.id) | quote }} + - name: "EDC_IDS_DESCRIPTION" + value: {{ .Values.runtime.internationalDataSpaces.description | quote }} + - name: "EDC_IDS_TITLE" + value: {{ .Values.runtime.internationalDataSpaces.title | quote }} + - name: "EDC_IDS_MAINTAINER" + value: {{ .Values.runtime.internationalDataSpaces.maintainer | quote }} + - name: "EDC_IDS_CURATOR" + value: {{ .Values.runtime.internationalDataSpaces.curator | quote }} + - name: "EDC_IDS_CATALOG_ID" + value: {{ printf "urn:catalog:%s" (lower .Values.runtime.internationalDataSpaces.catalogId) | quote }} + - name: "EDC_OAUTH_PROVIDER_AUDIENCE" + value: "idsc:IDS_CONNECTORS_ALL" + - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" + value: {{ printf "%s%s%s" (include "txdc.runtime.url.ids" . ) .Values.runtime.endpoints.ids.path "/data" | quote }} + # this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older + - name: "EDC_IDS_ENDPOINT_AUDIENCE" + value: {{ printf "%s%s%s" (include "txdc.runtime.url.ids" . ) .Values.runtime.endpoints.ids.path "/data" | quote }} + + ################ + ## DATA PLANE ## + ################ + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/dataplane-selector-configuration + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" + value: {{ include "txdc.dataplane.url.control" . }}/transfer + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" + value: "HttpData,AmazonS3" + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_DESTINATIONTYPES" + value: "HttpProxy,AmazonS3" + - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_PROPERTIES" + value: |- + {{ printf "{ \"publicApiUrl\": \"%s\" }" (include "txdc.dataplane.url.public" . ) }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/data-plane-transfer + - name: "EDC_TRANSFER_PROXY_ENDPOINT" + value: {{ include "txdc.dataplane.url.public" . }} + - name: "EDC_TRANSFER_PROXY_TOKEN_SIGNER_PRIVATEKEY_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenSignerPrivateKey | quote }} + - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} + + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/http-receiver + - name: "EDC_RECEIVER_HTTP_ENDPOINT" + value: {{ .Values.backendService.httpProxyTokenReceiverUrl | required ".Values.backendService.httpProxyTokenReceiverUrl is required" | quote }} + + ########### + ## VAULT ## + ########### + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault + - name: "SECRETS" + value: {{ .Values.vault.secrets | quote}} + + ##################### + ## DATA ENCRYPTION ## + ##################### + + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/data-encryption + - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" + value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} + - name: "EDC_DATA_ENCRYPTION_ALGORITHM" + value: "AES" + + ########################### + ## AAS WRAPPER EXTENSION ## + ########################### + - name: "EDC_CP_ADAPTER_CACHE_CATALOG_EXPIRE_AFTER" + value: "0" + - name: "EDC_CP_ADAPTER_REUSE_CONTRACT_AGREEMENT" + value: "0" + + ########################### + ## BUSINESS PARTNER NUMBER VALIDATION EXTENSION ## + ########################### + - name: "TRACTUSX_BUSINESSPARTNERVALIDATION_LOG_AGREEMENT_VALIDATION" + value: {{ .Values.runtime.businessPartnerValidation.log.agreementValidation | quote }} + + ###################################### + ## Additional environment variables ## + ###################################### + {{- range $key, $value := .Values.runtime.envValueFrom }} + - name: {{ $key | quote }} + valueFrom: + {{- tpl (toYaml $value) $ | nindent 16 }} + {{- end }} + {{- range $key, $value := .Values.runtime.env }} + - name: {{ $key | quote }} + value: {{ $value | quote }} + {{- end }} + {{- if and (or .Values.runtime.envSecretNames .Values.runtime.envConfigMapNames) (or (gt (len .Values.runtime.envSecretNames) 0) (gt (len .Values.runtime.envConfigMapNames) 0)) }} + envFrom: + {{- range $value := .Values.runtime.envSecretNames }} + - secretRef: + name: {{ $value | quote }} + {{- end }} + {{- range $value := .Values.runtime.envConfigMapNames }} + - configMapRef: + name: {{ $value | quote }} + {{- end }} + {{- end }} + volumeMounts: + - name: "configuration" + mountPath: "/app/logging.properties" + subPath: "logging.properties" + volumes: + - name: "configuration" + configMap: + name: {{ include "txdc.fullname" . }}-runtime + items: + - key: "logging.properties" + path: "logging.properties" + {{- with .Values.runtime.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.runtime.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.runtime.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/tractusx-connector-memory/templates/hpa-runtime.yaml b/charts/tractusx-connector-memory/templates/hpa-runtime.yaml new file mode 100644 index 000000000..a373dfb63 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/hpa-runtime.yaml @@ -0,0 +1,29 @@ +{{- if .Values.runtime.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "txdc.fullname" . }}-runtime + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "txdc.fullname" . }}-runtime + minReplicas: {{ .Values.runtime.autoscaling.minReplicas }} + maxReplicas: {{ .Values.runtime.autoscaling.maxReplicas }} + metrics: + {{- if .Values.runtime.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.runtime.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.runtime.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.runtime.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/edc-dataplane/templates/ingress.yaml b/charts/tractusx-connector-memory/templates/ingress-runtime.yaml similarity index 58% rename from charts/edc-dataplane/templates/ingress.yaml rename to charts/tractusx-connector-memory/templates/ingress-runtime.yaml index 716ac3d1f..06c6f5c68 100644 --- a/charts/edc-dataplane/templates/ingress.yaml +++ b/charts/tractusx-connector-memory/templates/ingress-runtime.yaml @@ -1,33 +1,12 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -{{- $fullName := include "edc-dataplane.fullname" . }} -{{- $labels := include "edc-dataplane.labels" . | nindent 4 }} +{{- $fullName := include "txdc.fullname" . }} +{{- $controlLabels := include "txdc.runtime.labels" . | nindent 4 }} +{{- $controlEdcEndpoints := .Values.runtime.endpoints }} {{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} -{{- $edcEndpoints := .Values.edc.endpoints }} {{- $namespace := .Release.Namespace }} -{{- range .Values.ingresses }} + +{{- range .Values.runtime.ingresses }} {{- if and .enabled .endpoints }} -{{- $ingressName := printf "%s-%s" $fullName .hostname }} +{{- $controlIngressName := printf "%s-runtime-%s" $fullName .hostname }} --- {{- if semverCompare ">=1.19-0" $gitVersion }} apiVersion: networking.k8s.io/v1 @@ -38,10 +17,10 @@ apiVersion: extensions/v1beta1 {{- end }} kind: Ingress metadata: - name: {{ $ingressName }} + name: {{ $controlIngressName }} namespace: {{ $namespace | default "default" | quote }} labels: - {{- $labels | nindent 2 }} + {{- $controlLabels | nindent 2 }} annotations: {{- if and .className (not (semverCompare ">=1.18-0" $gitVersion)) }} {{- if not (hasKey .annotations "kubernetes.io/ingress.class") }} @@ -71,7 +50,7 @@ spec: {{- if .tls.secretName }} secretName: {{ .tls.secretName }} {{- else }} - secretName: {{ $ingressName }}-tls + secretName: {{ $controlIngressName }}-tls {{- end }} {{- end }} rules: @@ -79,19 +58,17 @@ spec: http: paths: {{- $ingressEdcEndpoints := .endpoints }} - {{- range $name, $mapping := $edcEndpoints }} + {{- range $name, $mapping := $controlEdcEndpoints }} {{- if (has $name $ingressEdcEndpoints) }} - path: {{ $mapping.path }} pathType: Prefix backend: {{- if semverCompare ">=1.19-0" $gitVersion }} service: - name: {{ $fullName }} + name: {{ $fullName }}-runtime port: number: {{ $mapping.port }} {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $mapping.port }} {{- end }} {{- end }} {{- end }} diff --git a/charts/tractusx-connector-memory/templates/service-runtime.yaml b/charts/tractusx-connector-memory/templates/service-runtime.yaml new file mode 100644 index 000000000..241e28885 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/service-runtime.yaml @@ -0,0 +1,59 @@ +# + # Copyright (c) 2023 ZF Friedrichshafen AG + # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH + # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # 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 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + # License for the specific language governing permissions and limitations + # under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "txdc.fullname" . }}-runtime + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} +spec: + type: {{ .Values.runtime.service.type }} + ports: + - port: {{ .Values.runtime.endpoints.default.port }} + targetPort: default + protocol: TCP + name: default + - port: {{ .Values.runtime.endpoints.control.port }} + targetPort: control + protocol: TCP + name: control + - port: {{ .Values.runtime.endpoints.data.port }} + targetPort: data + protocol: TCP + name: data + - port: {{ .Values.runtime.endpoints.validation.port }} + targetPort: validation + protocol: TCP + name: validation + - port: {{ .Values.runtime.endpoints.ids.port }} + targetPort: ids + protocol: TCP + name: ids + - port: {{ .Values.runtime.endpoints.observability.port}} + targetPort: observability + protocol: TCP + name: observability + selector: + {{- include "txdc.runtime.selectorLabels" . | nindent 4 }} diff --git a/charts/tractusx-connector-memory/templates/serviceaccount.yaml b/charts/tractusx-connector-memory/templates/serviceaccount.yaml new file mode 100644 index 000000000..c650bcd68 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/serviceaccount.yaml @@ -0,0 +1,16 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "txdc.serviceAccountName" . }} + labels: + {{- include "txdc.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- with .Values.serviceAccount.imagePullSecrets }} +imagePullSecrets: + {{- toYaml . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml new file mode 100644 index 000000000..d98493953 --- /dev/null +++ b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "txdc.fullname" . }}-test-readiness" + labels: + {{- include "txdc.runtime.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: curlimages/curl + command: ['curl'] + args: ['{{ include "tdxc.runtime.url.readiness" . }}'] + restartPolicy: Never diff --git a/charts/tractusx-connector-memory/values.yaml b/charts/tractusx-connector-memory/values.yaml new file mode 100644 index 000000000..83ce92818 --- /dev/null +++ b/charts/tractusx-connector-memory/values.yaml @@ -0,0 +1,316 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +# Default values for eclipse-dataspace-connector. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +fullnameOverride: "" +nameOverride: "" + +# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) +imagePullSecrets: [] + +customLabels: {} + +runtime: + image: + repository: "" + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "" + initContainers: [] + debug: + enabled: false + port: 1044 + suspendOnStart: false + internationalDataSpaces: + id: TXDC + description: Tractus-X Eclipse IDS Data Space Connector + title: "" + maintainer: "" + curator: "" + catalogId: TXDC-Catalog + livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first liveness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + readinessProbe: + # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first readiness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a readiness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # -- endpoints of the control plane + endpoints: + # -- default api for health checks, should not be added to any ingress + default: + # -- port for incoming api calls + port: 8080 + # -- path for incoming api calls + path: /api + # -- data management api, used by internal users, can be added to an ingress and must not be internet facing + data: + # -- port for incoming api calls + port: 8081 + # -- path for incoming api calls + path: /data + # -- authentication key, must be attached to each 'X-Api-Key' request header + authKey: "" + # -- validation api, only used by the data plane and should not be added to any ingress + validation: + # -- port for incoming api calls + port: 8082 + # -- path for incoming api calls + path: /validation + # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not + control: + # -- port for incoming api calls + port: 8083 + # -- path for incoming api calls + path: /control + # -- ids api, used for inter connector communication and must be internet facing + ids: + # -- port for incoming api calls + port: 8084 + # -- path for incoming api calls + path: /api/v1/ids + # -- observability api with unsecured access, must not be internet facing + observability: + # -- port for incoming API calls + port: 8085 + # -- observability api, provides /health /readiness and /liveness endpoints + path: /observability + # -- allow or disallow insecure access, i.e. access without authentication + insecure: true + public: + port: 8086 + path: /api/public + businessPartnerValidation: + log: + agreementValidation: true + service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + annotations: {} + # -- additional labels for the pod + podLabels: {} + # -- additional annotations for the pod + podAnnotations: {} + # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment + podSecurityContext: + seccompProfile: + # -- Restrict a Container's Syscalls with seccomp + type: RuntimeDefault + # -- Runs all processes within a pod with a special uid + runAsUser: 10001 + # -- Processes within a pod will belong to this guid + runAsGroup: 10001 + # -- The owner for volumes and any files created within volumes will belong to this guid + fsGroup: 10001 + # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod + securityContext: + capabilities: + # -- Specifies which capabilities to drop to reduce syscall attack surface + drop: + - ALL + # -- Specifies which capabilities to add to issue specialized syscalls + add: [] + # -- Whether the root filesystem is mounted in read-only mode + readOnlyRootFilesystem: true + # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID + allowPrivilegeEscalation: false + # -- Requires the container to run without root privileges + runAsNonRoot: true + # -- The container's process will run with the specified uid + runAsUser: 10001 + # Extra environment variables that will be pass onto deployment pods + env: {} + # ENV_NAME: value + + # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # secretKeyRef: + # name: secret-name + # key: value_key + + # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from + envSecretNames: [] + # - first-secret + # - second-secret + + # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from + envConfigMapNames: [] + # - first-config-map + # - second-config-map + + ## Ingress declaration to expose the network service. + ingresses: + ## Public / Internet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.local" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - ids + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + ## Private / Intranet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.intranet" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - data + - control + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container + volumeMounts: [] + # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories + volumes: [] + # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container + resources: + {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + replicaCount: 1 + autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) + logging: |- + .level=INFO + org.eclipse.edc.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n + + # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes + nodeSelector: {} + # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes + tolerations: [] + # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on + affinity: {} + + url: + # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) + ids: "" + public: "" + readiness: "" + +vault: + # secrets can be seeded by supplying them in a comma separated list key1:secret2,key2:secret2 + secrets: "" + secretNames: + transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key + transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key + transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key + dapsPrivateKey: daps-private-key + dapsPublicKey: daps-public-key + +daps: + url: "" + clientId: "" + paths: + jwks: /jwks.json + token: /token + +backendService: + httpProxyTokenReceiverUrl: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + imagePullSecrets: [] diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index f9e4322c6..696e94396 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -36,12 +36,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.2 +version: 0.3.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.3.2" +appVersion: "0.3.3" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index af53087c9..12c45b649 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -1,6 +1,6 @@ # tractusx-connector -![Version: 0.3.2](https://img.shields.io/badge/Version-0.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.2](https://img.shields.io/badge/AppVersion-0.3.2-informational?style=flat-square) +![Version: 0.3.3](https://img.shields.io/badge/Version-0.3.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.3](https://img.shields.io/badge/AppVersion-0.3.3-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector @@ -10,7 +10,7 @@ A Helm chart for Tractus-X Eclipse Data Space Connector ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector --version 0.3.2 +helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 ``` ## Source Code @@ -28,23 +28,21 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.2 | controlplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | | controlplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | | controlplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| controlplane.businessPartnerValidation.log.agreementValidation | bool | `true` | | | controlplane.debug.enabled | bool | `false` | | | controlplane.debug.port | int | `1044` | | | controlplane.debug.suspendOnStart | bool | `false` | | -| controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"data":{"authKey":"","path":"/data","port":8081},"default":{"path":"/api","port":8080},"ids":{"path":"/api/v1/ids","port":8084},"metrics":{"path":"/metrics","port":9090},"observability":{"insecure":true,"path":"/observability","port":8085},"validation":{"path":"/validation","port":8082}}` | endpoints of the control plane | +| controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"observability":{"insecure":true,"path":"/observability","port":8085},"protocol":{"path":"/api/v1/ids","port":8084}}` | endpoints of the control plane | | controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | | controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | | controlplane.endpoints.control.port | int | `8083` | port for incoming api calls | -| controlplane.endpoints.data | object | `{"authKey":"","path":"/data","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | -| controlplane.endpoints.data.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | -| controlplane.endpoints.data.path | string | `"/data"` | path for incoming api calls | -| controlplane.endpoints.data.port | int | `8081` | port for incoming api calls | | controlplane.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | | controlplane.endpoints.default.path | string | `"/api"` | path for incoming api calls | | controlplane.endpoints.default.port | int | `8080` | port for incoming api calls | -| controlplane.endpoints.ids | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | -| controlplane.endpoints.ids.path | string | `"/api/v1/ids"` | path for incoming api calls | -| controlplane.endpoints.ids.port | int | `8084` | port for incoming api calls | +| controlplane.endpoints.management | object | `{"authKey":"","path":"/management","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | +| controlplane.endpoints.management.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | +| controlplane.endpoints.management.path | string | `"/management"` | path for incoming api calls | +| controlplane.endpoints.management.port | int | `8081` | port for incoming api calls | | controlplane.endpoints.metrics | object | `{"path":"/metrics","port":9090}` | metrics api, used for application metrics, must not be internet facing | | controlplane.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | | controlplane.endpoints.metrics.port | int | `9090` | port for incoming api calls | @@ -52,9 +50,9 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.2 | controlplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | | controlplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | | controlplane.endpoints.observability.port | int | `8085` | port for incoming API calls | -| controlplane.endpoints.validation | object | `{"path":"/validation","port":8082}` | validation api, only used by the data plane and should not be added to any ingress | -| controlplane.endpoints.validation.path | string | `"/validation"` | path for incoming api calls | -| controlplane.endpoints.validation.port | int | `8082` | port for incoming api calls | +| controlplane.endpoints.protocol | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | +| controlplane.endpoints.protocol.path | string | `"/api/v1/ids"` | path for incoming api calls | +| controlplane.endpoints.protocol.port | int | `8084` | port for incoming api calls | | controlplane.env | object | `{}` | | | controlplane.envConfigMapNames | list | `[]` | | | controlplane.envSecretNames | list | `[]` | | @@ -77,7 +75,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.2 | controlplane.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | | controlplane.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | | controlplane.ingresses[1].enabled | bool | `false` | | -| controlplane.ingresses[1].endpoints | list | `["data","control"]` | EDC endpoints exposed by this ingress resource | +| controlplane.ingresses[1].endpoints | list | `["management","control"]` | EDC endpoints exposed by this ingress resource | | controlplane.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | | controlplane.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | | controlplane.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | @@ -148,10 +146,11 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.2 | dataplane.endpoints.default.port | int | `8080` | | | dataplane.endpoints.metrics.path | string | `"/metrics"` | | | dataplane.endpoints.metrics.port | int | `9090` | | +| dataplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | +| dataplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | +| dataplane.endpoints.observability.port | int | `8085` | port for incoming API calls | | dataplane.endpoints.public.path | string | `"/api/public"` | | | dataplane.endpoints.public.port | int | `8081` | | -| dataplane.endpoints.validation.path | string | `"/validation"` | | -| dataplane.endpoints.validation.port | int | `8082` | | | dataplane.env | object | `{}` | | | dataplane.envConfigMapNames | list | `[]` | | | dataplane.envSecretNames | list | `[]` | | diff --git a/charts/tractusx-connector/templates/_helpers.tpl b/charts/tractusx-connector/templates/_helpers.tpl index ecc8ff1d2..701e6fc75 100644 --- a/charts/tractusx-connector/templates/_helpers.tpl +++ b/charts/tractusx-connector/templates/_helpers.tpl @@ -110,9 +110,9 @@ Create the name of the service account to use {{/* Control IDS URL */}} -{{- define "txdc.controlplane.url.ids" -}} -{{- if .Values.controlplane.url.ids }}{{/* if ids api url has been specified explicitly */}} -{{- .Values.controlplane.url.ids }} +{{- define "txdc.controlplane.url.protocol" -}} +{{- if .Values.controlplane.url.protocol }}{{/* if ids api url has been specified explicitly */}} +{{- .Values.controlplane.url.protocol }} {{- else }}{{/* else when ids api url has not been specified explicitly */}} {{- with (index .Values.controlplane.ingresses 0) }} {{- if .enabled }}{{/* if ingress enabled */}} @@ -122,17 +122,17 @@ Control IDS URL {{- printf "http://%s" .hostname -}} {{- end }}{{/* end if tls */}} {{- else }}{{/* else when ingress not enabled */}} -{{- printf "http://%s-controlplane:%v" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.ids.port -}} +{{- printf "http://%s-controlplane:%v" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.protocol.port -}} {{- end }}{{/* end if ingress */}} {{- end }}{{/* end with ingress */}} -{{- end }}{{/* end if .Values.controlplane.url.ids */}} +{{- end }}{{/* end if .Values.controlplane.url.protocol */}} {{- end }} {{/* Validation URL */}} {{- define "txdc.controlplane.url.validation" -}} -{{- printf "http://%s-controlplane:%v%s/token" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.validation.port $.Values.controlplane.endpoints.validation.path -}} +{{- printf "http://%s-controlplane:%v%s/token" ( include "txdc.fullname" $ ) $.Values.controlplane.endpoints.control.port $.Values.controlplane.endpoints.control.path -}} {{- end }} {{/* diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 691047c0f..daab957e4 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -62,13 +62,13 @@ spec: {{- if .Values.controlplane.image.repository }} image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.postgresql.enabled .Values.vault.hashicorp.enabled }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.postgresql.enabled .Values.vault.azure.enabled }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-postgresql:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-controlplane-postgresql:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.hashicorp.enabled }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.azure.enabled }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-controlplane-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-runtime-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else }} {{- fail "cannot choose control-plane image automatically based on configuration" }} {{- end }} @@ -128,45 +128,30 @@ spec: value: {{ printf "%s%s" .Values.daps.url .Values.daps.paths.token }} - name: EDC_OAUTH_PRIVATE_KEY_ALIAS value: {{ .Values.vault.secretNames.dapsPrivateKey | required ".Values.vault.secretNames.dapsPrivateKey is required" | quote }} - - name: EDC_OAUTH_PUBLIC_KEY_ALIAS + - name: EDC_OAUTH_CERTIFICATE_ALIAS value: {{ .Values.vault.secretNames.dapsPublicKey | required ".Values.vault.secretNames.dapsPublicKey is required" | quote }} ####### # API # ####### - name: "EDC_API_AUTH_KEY" - value: {{ .Values.controlplane.endpoints.data.authKey | required ".Values.controlplane.endpoints.data.authKey is required" | quote }} + value: {{ .Values.controlplane.endpoints.management.authKey | required ".Values.controlplane.endpoints.mangement.authKey is required" | quote }} - name: "WEB_HTTP_DEFAULT_PORT" value: {{ .Values.controlplane.endpoints.default.port | quote }} - name: "WEB_HTTP_DEFAULT_PATH" value: {{ .Values.controlplane.endpoints.default.path | quote }} - {{- if or (eq (substr 0 3 .Values.controlplane.image.tag) "0.1") (eq (substr 0 3 .Values.controlplane.image.tag) "0.2") }} - # WEB_HTTP_DATA_PORT is renamed to WEB_HTTP_MANAGEMENT_PORT from version 0.2.1 and newer - # we will keep both settings for downward capabilities - - name: "WEB_HTTP_DATA_PORT" - value: {{ .Values.controlplane.endpoints.data.port | quote }} - # WEB_HTTP_DATA_PATH is renamed to WEB_HTTP_MANAGEMENT_PATH from version 0.2.1 and newer - # we will keep both settings for downward capabilities - - name: "WEB_HTTP_DATA_PATH" - value: {{ .Values.controlplane.endpoints.data.path | quote }} - {{- else }} - name: "WEB_HTTP_MANAGEMENT_PORT" - value: {{ .Values.controlplane.endpoints.data.port | quote }} + value: {{ .Values.controlplane.endpoints.management.port | quote }} - name: "WEB_HTTP_MANAGEMENT_PATH" - value: {{ .Values.controlplane.endpoints.data.path | quote }} - {{- end }} - - name: "WEB_HTTP_VALIDATION_PORT" - value: {{ .Values.controlplane.endpoints.validation.port | quote }} - - name: "WEB_HTTP_VALIDATION_PATH" - value: {{ .Values.controlplane.endpoints.validation.path | quote }} + value: {{ .Values.controlplane.endpoints.management.path | quote }} - name: "WEB_HTTP_CONTROL_PORT" value: {{ .Values.controlplane.endpoints.control.port | quote }} - name: "WEB_HTTP_CONTROL_PATH" value: {{ .Values.controlplane.endpoints.control.path | quote }} - - name: "WEB_HTTP_IDS_PORT" - value: {{ .Values.controlplane.endpoints.ids.port | quote }} - - name: "WEB_HTTP_IDS_PATH" - value: {{ .Values.controlplane.endpoints.ids.path | quote }} + - name: "WEB_HTTP_PROTOCOL_PORT" + value: {{ .Values.controlplane.endpoints.protocol.port | quote }} + - name: "WEB_HTTP_PROTOCOL_PATH" + value: {{ .Values.controlplane.endpoints.protocol.path | quote }} - name: "WEB_HTTP_OBSERVABILITY_PORT" value: {{ .Values.controlplane.endpoints.observability.port | quote}} - name: "WEB_HTTP_OBSERVABILITY_PATH" @@ -178,9 +163,9 @@ spec: ## IDS ## ######### - name: "IDS_WEBHOOK_ADDRESS" - value: {{ include "txdc.controlplane.url.ids" . | quote }} + value: {{ include "txdc.controlplane.url.protocol" . | quote }} - name: "EDC_IDS_ENDPOINT" - value: {{ printf "%s%s" (include "txdc.controlplane.url.ids" .) .Values.controlplane.endpoints.ids.path | quote }} + value: {{ printf "%s%s" (include "txdc.controlplane.url.protocol" .) .Values.controlplane.endpoints.protocol.path | quote }} - name: "EDC_IDS_ID" value: {{ printf "urn:connector:%s" (lower .Values.controlplane.internationalDataSpaces.id) | quote }} - name: "EDC_IDS_DESCRIPTION" @@ -196,10 +181,10 @@ spec: - name: "EDC_OAUTH_PROVIDER_AUDIENCE" value: "idsc:IDS_CONNECTORS_ALL" - name: "EDC_OAUTH_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.controlplane.url.ids" . ) .Values.controlplane.endpoints.ids.path "/data" | quote }} + value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} # this is the old setting name for 'EDC_OAUTH_ENDPOINT_AUDIENCE' and is mandatory for Produce EDC v0.1.2 and older - name: "EDC_IDS_ENDPOINT_AUDIENCE" - value: {{ printf "%s%s%s" (include "txdc.controlplane.url.ids" . ) .Values.controlplane.endpoints.ids.path "/data" | quote }} + value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} {{- if .Values.postgresql.enabled }} @@ -262,7 +247,7 @@ spec: ## DATA PLANE ## ################ - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/dataplane-selector-configuration + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/dataplane-selector-configuration - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_URL" value: {{ include "txdc.dataplane.url.control" . }}/transfer - name: "EDC_DATAPLANE_SELECTOR_DEFAULTPLANE_SOURCETYPES" @@ -281,9 +266,9 @@ spec: - name: "EDC_TRANSFER_PROXY_TOKEN_VERIFIER_PUBLICKEY_ALIAS" value: {{ .Values.vault.secretNames.transferProxyTokenSignerPublicKey | quote }} - # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/http-receiver + # see extension https://github.com/eclipse-edc/Connector/tree/main/extensions/control-plane/transfer/transfer-pull-http-dynamic-receiver - - name: "EDC_RECEIVER_HTTP_ENDPOINT" + - name: "EDC_RECEIVER_HTTP_DYNAMIC_ENDPOINT" value: {{ .Values.backendService.httpProxyTokenReceiverUrl | required ".Values.backendService.httpProxyTokenReceiverUrl is required" | quote }} ########### @@ -291,7 +276,7 @@ spec: ########### {{- if .Values.vault.hashicorp.enabled }} - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault - name: "EDC_VAULT_HASHICORP_URL" value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} - name: "EDC_VAULT_HASHICORP_TOKEN" @@ -332,7 +317,7 @@ spec: ## DATA ENCRYPTION ## ##################### - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/data-encryption + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/data-encryption - name: "EDC_DATA_ENCRYPTION_KEYS_ALIAS" value: {{ .Values.vault.secretNames.transferProxyTokenEncryptionAesKey | quote }} - name: "EDC_DATA_ENCRYPTION_ALGORITHM" @@ -346,6 +331,12 @@ spec: - name: "EDC_CP_ADAPTER_REUSE_CONTRACT_AGREEMENT" value: "0" + ########################### + ## BUSINESS PARTNER NUMBER VALIDATION EXTENSION ## + ########################### + - name: "TRACTUSX_BUSINESSPARTNERVALIDATION_LOG_AGREEMENT_VALIDATION" + value: {{ .Values.controlplane.businessPartnerValidation.log.agreementValidation | quote }} + ###################################### ## Additional environment variables ## ###################################### diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index ff5f6a5ce..bbc48c434 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + --- apiVersion: apps/v1 kind: Deployment @@ -40,9 +62,9 @@ spec: {{- if .Values.dataplane.image.repository }} image: "{{ .Values.dataplane.image.repository }}:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else if and .Values.vault.hashicorp }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else if .Values.vault.azure }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else }} {{- fail "cannot choose data-plane image automatically based on configuration" }} {{- end }} @@ -56,8 +78,8 @@ spec: {{- if .Values.dataplane.livenessProbe.enabled }} livenessProbe: httpGet: - path: {{ .Values.dataplane.endpoints.default.path }}/check/liveness - port: {{ .Values.dataplane.endpoints.default.port }} + path: {{ .Values.dataplane.endpoints.observability.path }}/check/liveness + port: {{ .Values.dataplane.endpoints.observability.port }} initialDelaySeconds: {{ .Values.dataplane.livenessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.dataplane.livenessProbe.periodSeconds }} timeoutSeconds: {{ .Values.dataplane.livenessProbe.timeoutSeconds }} @@ -67,8 +89,8 @@ spec: {{- if .Values.dataplane.readinessProbe.enabled }} readinessProbe: httpGet: - path: {{ .Values.dataplane.endpoints.default.path }}/check/readiness - port: {{ .Values.dataplane.endpoints.default.port }} + path: {{ .Values.dataplane.endpoints.observability.path }}/check/readiness + port: {{ .Values.dataplane.endpoints.observability.port }} initialDelaySeconds: {{ .Values.dataplane.readinessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.dataplane.readinessProbe.periodSeconds }} timeoutSeconds: {{ .Values.dataplane.readinessProbe.timeoutSeconds }} @@ -100,16 +122,18 @@ spec: value: {{ .Values.dataplane.endpoints.control.port | quote }} - name: "WEB_HTTP_CONTROL_PATH" value: {{ .Values.dataplane.endpoints.control.path | quote }} - - name: "WEB_HTTP_VALIDATION_PORT" - value: {{ .Values.dataplane.endpoints.validation.port | quote }} - - name: "WEB_HTTP_VALIDATION_PATH" - value: {{ .Values.dataplane.endpoints.validation.path | quote }} - name: "WEB_HTTP_PUBLIC_PORT" value: {{ .Values.dataplane.endpoints.public.port | quote }} - name: "WEB_HTTP_PUBLIC_PATH" value: {{ .Values.dataplane.endpoints.public.path | quote }} - name: "EDC_DATAPLANE_TOKEN_VALIDATION_ENDPOINT" - value: {{ include "txdc.controlplane.url.validation" .}} + value: {{ include "txdc.controlplane.url.validation" .}} + - name: "WEB_HTTP_OBSERVABILITY_PORT" + value: {{ .Values.dataplane.endpoints.observability.port | quote }} + - name: "WEB_HTTP_OBSERVABILITY_PATH" + value: {{ .Values.dataplane.endpoints.observability.path | quote }} + - name: "TRACTUSX_API_OBSERVABILITY_ALLOW-INSECURE" + value: {{ .Values.dataplane.endpoints.observability.insecure | quote }} ####### # AWS # @@ -132,7 +156,7 @@ spec: ########### {{- if .Values.vault.hashicorp.enabled }} - # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/edc-extensions/hashicorp-vault + # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault - name: "EDC_VAULT_HASHICORP_URL" value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} - name: "EDC_VAULT_HASHICORP_TOKEN" @@ -156,15 +180,21 @@ spec: value: {{ .Values.vault.azure.tenant | quote }} - name: "EDC_VAULT_NAME" value: {{ .Values.vault.azure.name | quote }} + # only set the env var if config value not null + {{- if .Values.vault.azure.secret }} - name: "EDC_VAULT_CLIENTSECRET" value: {{ .Values.vault.azure.secret | quote }} + {{- end }} + # only set the env var if config value not null + {{- if .Values.vault.azure.certificate }} - name: "EDC_VAULT_CERTIFICATE" value: {{ .Values.vault.azure.certificate | quote }} + {{- end }} {{- end }} - ###################################### - ## Additional environment variables ## - ###################################### + ###################################### + ## Additional environment variables ## + ###################################### {{- range $key, $value := .Values.dataplane.envValueFrom }} - name: {{ $key | quote }} valueFrom: diff --git a/charts/tractusx-connector/templates/service-controlplane.yaml b/charts/tractusx-connector/templates/service-controlplane.yaml index 94a02fa1e..acab58343 100644 --- a/charts/tractusx-connector/templates/service-controlplane.yaml +++ b/charts/tractusx-connector/templates/service-controlplane.yaml @@ -39,18 +39,14 @@ spec: targetPort: control protocol: TCP name: control - - port: {{ .Values.controlplane.endpoints.data.port }} - targetPort: data + - port: {{ .Values.controlplane.endpoints.management.port }} + targetPort: management protocol: TCP - name: data - - port: {{ .Values.controlplane.endpoints.validation.port }} - targetPort: validation + name: management + - port: {{ .Values.controlplane.endpoints.protocol.port }} + targetPort: protocol protocol: TCP - name: validation - - port: {{ .Values.controlplane.endpoints.ids.port }} - targetPort: ids - protocol: TCP - name: ids + name: protocol - port: {{ .Values.controlplane.endpoints.metrics.port }} targetPort: metrics protocol: TCP diff --git a/charts/tractusx-connector/templates/service-dataplane.yaml b/charts/tractusx-connector/templates/service-dataplane.yaml index 26fa9c203..5644f7fbe 100644 --- a/charts/tractusx-connector/templates/service-dataplane.yaml +++ b/charts/tractusx-connector/templates/service-dataplane.yaml @@ -21,6 +21,10 @@ spec: targetPort: public protocol: TCP name: public + - port: {{ .Values.dataplane.endpoints.observability.port }} + targetPort: observability + protocol: TCP + name: observability - port: {{ .Values.dataplane.endpoints.metrics.port }} targetPort: metrics protocol: TCP diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index cbc266a94..21acfc20b 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -89,19 +89,13 @@ controlplane: # -- path for incoming api calls path: /api # -- data management api, used by internal users, can be added to an ingress and must not be internet facing - data: + management: # -- port for incoming api calls port: 8081 # -- path for incoming api calls - path: /data + path: /management # -- authentication key, must be attached to each 'X-Api-Key' request header authKey: "" - # -- validation api, only used by the data plane and should not be added to any ingress - validation: - # -- port for incoming api calls - port: 8082 - # -- path for incoming api calls - path: /validation # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not control: # -- port for incoming api calls @@ -109,7 +103,7 @@ controlplane: # -- path for incoming api calls path: /control # -- ids api, used for inter connector communication and must be internet facing - ids: + protocol: # -- port for incoming api calls port: 8084 # -- path for incoming api calls @@ -128,6 +122,9 @@ controlplane: path: /observability # -- allow or disallow insecure access, i.e. access without authentication insecure: true + businessPartnerValidation: + log: + agreementValidation: true service: # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. type: ClusterIP @@ -221,7 +218,7 @@ controlplane: annotations: {} # -- EDC endpoints exposed by this ingress resource endpoints: - - data + - management - control # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use className: "" @@ -340,12 +337,16 @@ dataplane: public: port: 8081 path: /api/public - validation: - port: 8082 - path: /validation control: port: 8083 path: /api/dataplane/control + observability: + # -- port for incoming API calls + port: 8085 + # -- observability api, provides /health /readiness and /liveness endpoints + path: /observability + # -- allow or disallow insecure access, i.e. access without authentication + insecure: true metrics: port: 9090 path: /metrics diff --git a/docs/README.md b/docs/README.md index 259c2560b..17ad45b14 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,10 @@ # Tractus-X EDC -The Tractus-X EDC repository creates runnable applications out of EDC extensions from the [Eclipse DataSpace Connector](https://github.com/eclipse-edc/Connector) repository. +The Tractus-X EDC repository creates runnable applications out of EDC extensions from +the [Eclipse DataSpace Connector](https://github.com/eclipse-edc/Connector) repository. -When running a EDC connector from the Tractus-X EDC repository there are three setups to choose from. They only vary by using different extensions for +When running a EDC connector from the Tractus-X EDC repository there are three setups to choose from. They only vary by +using different extensions for - Resolving of Connector-Identities - Persistence of the Control-Plane-State @@ -12,11 +14,11 @@ When running a EDC connector from the Tractus-X EDC repository there are three s The three supported setups are. -- Setup 1: In Memory & Azure Vault - - [Control Plane](../edc-controlplane/edc-controlplane-memory/README.md) +- Setup 1: Pure in Memory **Not intended for production use!** + - [Control Plane](../edc-controlplane/edc-runtime-memory/README.md) - [IDS DAPS Extensions](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/iam/oauth2/daps) - In Memory Persistence done by using no extension - - [Azure Key Vault Extension](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/vault/azure-vault) + - In Memory Keyvault with seedable secrets. - [Data Plane](../edc-dataplane/edc-dataplane-azure-vault/README.md) - [Azure Key Vault Extension](https://github.com/eclipse-edc/Connector/tree/main/extensions/common/vault/azure-vault) - Setup 2: PostgreSQL & Azure Vault diff --git a/docs/development/decision-records/2023-02-09-release-process/README.md b/docs/development/decision-records/2023-02-09-release-process/README.md index aee5bac5a..4b2771c0a 100644 --- a/docs/development/decision-records/2023-02-09-release-process/README.md +++ b/docs/development/decision-records/2023-02-09-release-process/README.md @@ -8,7 +8,7 @@ To improve stability, reproducibility and maintainability of releases, tractusx- - use release versions of EDC in releases. Release branches must not change upstream dependency versions, unless there is a clear and concise reason to do so. - slightly update branching model -- if possible, bugs/defects should be fixed on `develop` and be backported to the respective `hotfix/` branch +- if possible, bugs/defects should be fixed on `main` and be backported to the respective `hotfix/` branch - only hotfixes for critical security bugs will be provided as defined by the committers for the currently released version. Nothing else. - feature development happens _in developers' forks only_ to keep the Git reflog of the `origin` clean. @@ -31,15 +31,15 @@ Every release version published by tractusx-edc must be reproducible at any time During feature development we only use `-SNAPSHOT` versions of EDC packages. It is assumed that when the build breaks due to changes in upstream, the fix can be done quickly and easily, much more so than working off technical -debt that would otherwise accumulate over several months. Builds on `develop` are therefore _not repeatable_, but that +debt that would otherwise accumulate over several months. Builds on `main` are therefore _not repeatable_, but that downside is easily offset by the tighter alignment with and smaller technical debt and integration pain with the upstream EDC. ### Use release versions of EDC in releases -First, a new branch `releases/X.Y.Z` based off of `develop` is created. This can either be done +First, a new branch `release/X.Y.Z` based off of `main` is created. This can either be done on `HEAD`, or - if desired - on a particular ref. The latter case is relevant if there are already features -in `develop` that are not scoped for a particular release. +in `main` that are not scoped for a particular release. Second, the dependency onto EDC is updated to the most recent build. For example, if a release is created on March 27th 2023, the most recent nightly would be `0.0.1-20230326`. @@ -79,13 +79,13 @@ Once a release is published, for example `0.3.1` it will receive no further deve hotfix branches are created based off of the release branch, here `releases/0.3.1`, thus, `hotfix/0.3.1`. From this, three scenarios emerge: -1. The actual fix is done on `develop` and can be cherry-picked into the `hotfix/0.3.1` branch. No new commits are +1. The actual fix is done on `main` and can be cherry-picked into the `hotfix/0.3.1` branch. No new commits are made directly in that branch. -2. The actual fix is done on `develop` and must be manually ported into the `hotfix/0.3.1` branch. One or several new +2. The actual fix is done on `main` and must be manually ported into the `hotfix/0.3.1` branch. One or several new commits are made on `hotfix/0.3.1`. This is needed when cherry-picking is not available due to incompatibilities - between `develop` and the hotfix branch due to intermittent changes. -3. The fix is only relevant for the `0.3.1` hotfix, it is not needed in `develop`. This can happen, when the problem is - not present on `develop`, because it was already implicitly fixed, or otherwise doesn't exist. + between `main` and the hotfix branch due to intermittent changes. +3. The fix is only relevant for the `0.3.1` hotfix, it is not needed in `main`. This can happen, when the problem is + not present on `main`, because it was already implicitly fixed, or otherwise doesn't exist. This might produce many branches, and the first `hotfix` makes the release obsolete, but it will greatly help readability and make a release's history readily apparent. diff --git a/docs/development/decision-records/2023-02-27_testing/README.md b/docs/development/decision-records/2023-02-27_testing/README.md index 45844203d..0d12ab353 100644 --- a/docs/development/decision-records/2023-02-27_testing/README.md +++ b/docs/development/decision-records/2023-02-27_testing/README.md @@ -82,5 +82,5 @@ This section explains _at which point in time_ we should execute which test. Thi | Unit test | when running tests locally, without any parameters, on every commit on every branch | | | Integration test | on every commit on every branch | | | System/End-To-End test | on pull request branches except when marked as `draft` | | -| Deployment test | before merging pull requests and on every commit on `develop` | | +| Deployment test | before merging pull requests and on every commit on `main` | | | Performance test | Only on a specific schedule, e.g. once per day or week | | diff --git a/docs/development/decision-records/2023-04-03_renaming_branches/README.md b/docs/development/decision-records/2023-04-03_renaming_branches/README.md new file mode 100644 index 000000000..5638a79dd --- /dev/null +++ b/docs/development/decision-records/2023-04-03_renaming_branches/README.md @@ -0,0 +1,61 @@ +# Renaming Git branches to comply with TractusX standards + +## Decision + +TractusX-EDC will rename its Git branching structure to comply with TractusX release guidelines, and to be able to +leverage +GitHub convenience features, while continuing to use the Gitflow branching model. + +## Rationale + +The TractusX organization has established +a [release guideline](https://eclipse-tractusx.github.io/docs/release/trg-2/trg-2-1/) which mandates that all projects' +default branch be called `main`. + +### Selecting default branches + +In GitHub, the default branch has a couple of important features attached to it: + +- cloning or forking the repository will automatically check out the default branch +- when creating pull requests the default branch is targeted by default +- [automatic issue linking and closing](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) + only works with the default branch + +### The problem with GitFlow + +The GitFlow branching model suggests that the day-to-day work be done on a branch called `develop`, while the `main` +branch stores the version history and only receives (merge) commits after a version releases. + +This would call for `develop` being the GitHub default branch, which is forbidden by the aforementioned release +guideline. + +## Approach + +In order to comply with the TractusX release guideline, to make use of the GitHub features _and_ also use GitFlow, we +propose renaming a couple of branches. While GitFlow _suggests_ branch names, it does not _require_ it, and most +tools allow for customizing them anyway. Thus, from an abstract perspective, the following changes are necessary: + +- `main` becomes our work/development branch. All pull requests target `main`. +- `develop` gets deleted +- a new branch `releases` is introduced, which tracks the release history and receives post-release merge commits. + +Technically this will involve force-pushing, which is a potentially destructive operation. Therefor the following +section outlines the exact sequence of steps. Note that "upstream" refers to `eclipse-tractusx/tractusx-edc`, while " +fork" refers to `catenax-ng/tx-tractusx-edc`. + +- create a new branch `upstream/releases` +- create a new branch `fork/releases`, set it to track `upstream/releases` +- push the contents of `fork/main` -> `upstream/releases` +- synchronize `upstream/develop` with `fork/develop` +- force-push the contents of `develop` -> `upstream/main` (do **not** update the tracking branch!) +- synchronize `upstream/main` -> `fork/main` +- delete/archive `upstream/develop` and `fork/develop` + +_Note that most of this will likely need to be done manually, since GitHub does not allow for advanced Git operations +like force-pushing. Write access to `upstream` is required!_ + +## Further notes + +The new `releases` branch (note the plural) will serve the same purpose that `main` did up until now, which is to track +all releases (via merge commits and tags) in chronological order. We will continue to have separate `release/x.y.z` +branches for every release. diff --git a/docs/development/decision-records/2023-04-11_refactor_helmcharts/README.md b/docs/development/decision-records/2023-04-11_refactor_helmcharts/README.md new file mode 100644 index 000000000..5cf59f958 --- /dev/null +++ b/docs/development/decision-records/2023-04-11_refactor_helmcharts/README.md @@ -0,0 +1,112 @@ +# Refactor TractusX-EDC Helm charts + +## Decision + +The Helm charts provided by Tractusx-EDC will be refactored to be more focused and opinionated. Specifically, there will +be the following charts: + +1. `tractusx-connector-memory`: all backing stores are memory-based and thus ephemeral. The vault will also be + memory-based. _This chart is intended for testing/demo purposes only!_ +2. `tractusx-connector`: this is the "production-ready" chart that uses PostgreSQL and Hashicorp-Vault +3. `tractusx-connector-azure-vault`: this is a variant of `tractusx-connector-azure-vault` that uses Azure KeyVault (" + AZKV") instead + of Hashicorp as some stakeholders still use AZKV. + +These charts and their default configuration will be fully [tested](#testing). + +In addition to that, the Docker images will undergo some [refactoring](#docker-image-refactoring) as well. + +## Rationale + +The current "dynamically composed" helm chart has proven to be a source for issues, and it is difficult to isolate +errors due to the great number of variations. Further, only one particular variant (i.e. postgres+hashicorp) is put to +any semblance of testing (i.e. business tests). + +The official recommendation of TractusX-EDC is to use PostgreSQL and HashiCorp Vault, and alongside it, we will provide +charts for easy testing and setting up demos as well as an Azure KeyVault variant for legacy use cases. + +> Note: using Azure KeyVault is not officially supported or recommended by TractusX-EDC! + +This will also reduce the number of Docker images that need to be published. + +## Approach + +### Variant 1: `tractusx-connector-memory` + +This chart is intended for blackbox-testing or for easily setting up demos etc. It is **not** recommended for anything +else. It will have the following properties: + +- all backing stores (Asset Index, Policy Store etc.) are ephemeral in-memory stores +- the vault implementation will either be based also on memory, or on the `FsVault`, which uses local storage to store + secrets +- an embedded data plane will be used +- no scalability or replication is possible +- DAPS will be used as identity provider, so there is an implicit dependency onto a DAPS instance +- the `edc-runtime-memory` Docker image will be used. That image contains both control plane and data plane. + +### Variant 2: `tractusx-connector` + +This is the production-ready chart that is published by TractusX-EDC, and it will actually consist of two charts. One is +the `tractusx-runtime` sub-chart, that contains all configuration for data plane and control plane, and the other one is +the top-level `tractusx-connector` chart, that pulls in other charts as dependencies that are needed for one TractusX +connector application. This is sometimes referred to +as ["umbrella chart"](https://helm.sh/docs/howto/charts_tips_and_tricks/#complex-charts-with-many-dependencies). + +> Note: this will **not** include sub-charts for DAPS or MinIO. + +```shell +tractusx-connector + |-> tractusx-runtime + |-> postgres + |-> hashicorp-vault +``` + +The `tractusx-runtime` chart has the following properties: + +- PostgreSQL is used as persistence backend +- HashiCorp Vault is used as secret store +- the data plane is a separate runtime, i.e. separate pod +- DAPS is used as identity provider +- the `edc-controlplane-postgresql-hashicorp-vault` and `edc-dataplane-hashicorp-vault` Docker images will be used + +### Variant 3: `tractusx-connector-azure-vault` + +This variant is essentially identical to `tractusx-connector` except for dropping the HashiCorp Vault chart, and +replacing the HashiCorp Vault configuration with Azure KeyVault configuration. + +For this, the `edc-controlplane-postgresql-azure-vault` and `edc-dataplane-azure-vault` Docker images will be used. + +### Testing + +There are several steps to testing our Helm charts: + +1. waiting for all pods to come up: using an exemplary configuration, this relies on the health checks, i.e. liveness + and readiness probe (i.e. the runtime`s observability endpoints) to ensure that (most of) the static + configuration is correct, no values are missing etc. +2. executing a set of HTTP requests against the management API and assert a successful HTTP status code. For that we + use [Helm chart tests](https://helm.sh/docs/topics/chart_tests/) + +> Note: we refer to this kind of testing as "deployment testing" + +### Docker image refactoring + +The following changes need to be made to our Docker images: + +- rename `edc-controlplane-memory` -> `-edc-runtime-memory` +- in `edc-runtime-memory` use `FsVault` instead of `AzureVault` +- `edc-runtime-memory` contains an embedded data plane +- rename `edc-controlplane-postgresql` -> `edc-controlplane-postgresql-azure-vault` +- delete `edc-controlplane-memory-hashicorp-vault` + +thus effectively resulting in the following structure: + +```shell +edc-controlplane +|-> edc-runtime-memory +|-> edc-controlplane-postgresql-hashicorp-vault +|-> edc-controlplane-postgresql-azure-vault + +edc-dataplane +|-> edc-dataplane-hashicorp-vault +|-> edc-dataplane-azure-vaul +``` diff --git a/docs/migration/Version_0.3.1_0.3.2.md b/docs/migration/Version_0.3.1_0.3.2.md index d6f49da29..4099e8c4b 100644 --- a/docs/migration/Version_0.3.1_0.3.2.md +++ b/docs/migration/Version_0.3.1_0.3.2.md @@ -2,7 +2,7 @@ ## Configuration of Azure KeyVault -When using Helm Charts that use the Azure KeyVault (`edc-controlplane-memory`, `edc-controlplane-postgres`) +When using Helm Charts that use the Azure KeyVault (`edc-runtime-memory`, `edc-controlplane-postgres`) it is now possible to select _either_ authentication via Client Secret (`azure.vault.secret`) or via certificate (`azure.vault.certificate`). diff --git a/edc-controlplane/build.gradle.kts b/edc-controlplane/build.gradle.kts index d27dbdf36..d7306dd1e 100644 --- a/edc-controlplane/build.gradle.kts +++ b/edc-controlplane/build.gradle.kts @@ -1,11 +1,10 @@ - plugins { `java-library` } dependencies { implementation(project(":edc-controlplane:edc-controlplane-base")) - implementation(project(":edc-controlplane:edc-controlplane-memory")) + implementation(project(":edc-controlplane:edc-runtime-memory")) implementation(project(":edc-controlplane:edc-controlplane-memory-hashicorp-vault")) implementation(project(":edc-controlplane:edc-controlplane-postgresql")) implementation(project(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault")) diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts index eb7aef8fe..3fb52b522 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/build.gradle.kts @@ -3,7 +3,7 @@ import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md new file mode 100644 index 000000000..e6088f521 --- /dev/null +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/notice.md @@ -0,0 +1,28 @@ +# Notice for Docker image + +An EDC Control Plane using memory-based storage, and HashiCorp Vault as secret store. + +DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-memory-hashicorp-vault + +Eclipse Tractus-X product(s) installed within the image: + +## TractusX-EDC Control Plane + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) + +## Used base image + +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. diff --git a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile index 229c44868..149256182 100644 --- a/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-memory-hashicorp-vault/src/main/docker/Dockerfile @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts index 7c8a46f54..637b63765 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/build.gradle.kts @@ -4,7 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md new file mode 100644 index 000000000..a1c9978ab --- /dev/null +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/notice.md @@ -0,0 +1,28 @@ +# Notice for Docker image + +An EDC Control Plane using PostgreSQL as persistence backend, and HashiCorp Vault as secret store. + +DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql-hashicorp-vault + +Eclipse Tractus-X product(s) installed within the image: + +## TractusX-EDC Control Plane + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) + +## Used base image + +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. diff --git a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile index b3e04fac7..d248e8131 100644 --- a/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts index 659aa6891..5888c34c4 100644 --- a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts @@ -4,7 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { @@ -12,9 +12,10 @@ dependencies { runtimeOnly(project(":edc-extensions:postgresql-migration")) runtimeOnly(edc.azure.vault) runtimeOnly(edc.bundles.sqlstores) + runtimeOnly(edc.transaction.local) + runtimeOnly(edc.sql.pool) runtimeOnly(edc.core.controlplane) runtimeOnly(edc.dpf.transfer) - } diff --git a/edc-controlplane/edc-controlplane-postgresql/notice.md b/edc-controlplane/edc-controlplane-postgresql/notice.md new file mode 100644 index 000000000..e1662d799 --- /dev/null +++ b/edc-controlplane/edc-controlplane-postgresql/notice.md @@ -0,0 +1,28 @@ +# Notice for Docker image + +An EDC Control Plane using PostgreSQL as persistence backend, and Azure KeyVault as secret store. + +DockerHub: https://hub.docker.com/r/tractusx/edc-controlplane-postgresql + +Eclipse Tractus-X product(s) installed within the image: + +## TractusX-EDC Control Plane + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) + +## Used base image + +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. diff --git a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile index b3e04fac7..d248e8131 100644 --- a/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-controlplane-postgresql/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-controlplane/edc-controlplane-memory/README.md b/edc-controlplane/edc-runtime-memory/README.md similarity index 54% rename from edc-controlplane/edc-controlplane-memory/README.md rename to edc-controlplane/edc-runtime-memory/README.md index ca1f0bef7..caf7fe24e 100644 --- a/edc-controlplane/edc-controlplane-memory/README.md +++ b/edc-controlplane/edc-runtime-memory/README.md @@ -1,54 +1,64 @@ # EDC Control-Plane backed by In-Memory Stores +## Security + +### In-memory Vault implementation + +The goal of this extension is to provide an ephemeral, memory-based vault implementation that can be used in testing or +demo scenarios. + +Please not that this vault does not encrypt the secrets, they are held in memory in plain text at runtime! In addition, +its ephemeral nature makes it unsuitable for replicated/multi-instance scenarios, i.e. Kubernetes. + +> It is not a secure secret store, please do NOT use it in production workloads! + ## Building ```shell -./gradlew :edc-controlplane:edc-controlplane-memory:dockerize +./gradlew :edc-controlplane:edc-runtime-memory:dockerize ``` ## Configuration (configuration.properties) -Listed below are configuration keys needed to get the `edc-controlplane-memory` up and running. -Details regarding each configuration property can be found at the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). - -| Key | Required | Example | Description | -|--------------------------------------------------|----------|--------------------------------------|----------------------------| -| edc.api.auth.key | | password | default value: random UUID | -| web.http.default.port | X | 8080 | | -| web.http.default.path | X | /api | | -| web.http.data.port | X | 8181 | | -| web.http.data.path | X | /data | | -| web.http.validation.port | X | 8182 | | -| web.http.validation.path | X | /validation | | -| web.http.control.port | X | 9999 | | -| web.http.control.path | X | /api/controlplane/control | | -| web.http.ids.port | X | 8282 | | -| web.http.ids.path | X | /api/v1/ids | | -| edc.receiver.http.endpoint | X | | | -| edc.ids.title | | Eclipse Dataspace Connector | | -| edc.ids.description | | Eclipse Dataspace Connector | | -| edc.ids.id | | urn:connector:edc | | -| edc.ids.security.profile | | base | | -| edc.ids.endpoint | | | | -| edc.ids.maintainer | | | | -| edc.ids.curator | | | | -| edc.ids.catalog.id | | urn:catalog:default | | -| ids.webhook.address | | | | -| edc.hostname | | localhost | | -| edc.oauth.token.url | X | | | -| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | -| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | -| edc.oauth.client.id | X | daps-oauth-client-id | | -| edc.vault.clientid | X | 00000000-1111-2222-3333-444444444444 | | -| edc.vault.tenantid | X | 55555555-6666-7777-8888-999999999999 | | -| edc.vault.name | X | my-vault-name | | -| edc.vault.clientsecret | X | 34-chars-secret | | -| edc.transfer.proxy.endpoint | X | | | -| edc.transfer.proxy.token.signer.privatekey.alias | X | | | +Listed below are configuration keys needed to get the `edc-runtime-memory` up and running. +Details regarding each configuration property can be found at +the [documentary section of the EDC](https://github.com/eclipse-edc/Connector/tree/main/docs). + +| Key | Required | Example | Description | +|--------------------------------------------------|----------|-------------------------------------|----------------------------| +| edc.api.auth.key | | password | default value: random UUID | +| web.http.default.port | X | 8080 | | +| web.http.default.path | X | /api | | +| web.http.data.port | X | 8181 | | +| web.http.data.path | X | /data | | +| web.http.validation.port | X | 8182 | | +| web.http.validation.path | X | /validation | | +| web.http.control.port | X | 9999 | | +| web.http.control.path | X | /api/controlplane/control | | +| web.http.ids.port | X | 8282 | | +| web.http.ids.path | X | /api/v1/ids | | +| edc.receiver.http.endpoint | X | | | +| edc.ids.title | | Eclipse Dataspace Connector | | +| edc.ids.description | | Eclipse Dataspace Connector | | +| edc.ids.id | | urn:connector:edc | | +| edc.ids.security.profile | | base | | +| edc.ids.endpoint | | | | +| edc.ids.maintainer | | | | +| edc.ids.curator | | | | +| edc.ids.catalog.id | | urn:catalog:default | | +| ids.webhook.address | | | | +| edc.hostname | | localhost | | +| edc.oauth.token.url | X | | | +| edc.oauth.public.key.alias | X | key-to-daps-certificate-in-keyvault | | +| edc.oauth.private.key.alias | X | key-to-private-key-in-keyvault | | +| edc.oauth.client.id | X | daps-oauth-client-id | | +| edc.transfer.proxy.endpoint | X | | | +| edc.transfer.proxy.token.signer.privatekey.alias | X | | | ### Example configuration.properties -JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` within the container. +JDK properties-style configuration of the EDC Control-Plane is expected to be mounted to `/app/configuration.properties` +within the container. ```shell # Create configuration.properties @@ -88,12 +98,6 @@ edc.oauth.public.key.alias=key-to-daps-certificate-in-keyvault edc.oauth.private.key.alias=key-to-private-key-in-keyvault edc.oauth.client.id=daps-oauth-client-id -# Azure vault related configuration -edc.vault.clientid=00000000-1111-2222-3333-444444444444 -edc.vault.tenantid=55555555-6666-7777-8888-999999999999 -edc.vault.name=my-vault-name -edc.vault.clientsecret=34-chars-secret - # Control- / Data- Plane configuration edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public edc.transfer.proxy.token.signer.privatekey.alias=azure-vault-token-signer-private-key @@ -115,24 +119,13 @@ java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [ EOF ``` -### Example opentelemetry.properties - -```shell -# Create opentelemetry.properties -export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) -cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} -otel.javaagent.enabled=true -otel.javaagent.debug=false -EOF -``` - ## Running ```shell docker run \ + -e SECRETS="key1:secret1,key2:secret2" \ -p 8080:8080 -p 8181:8181 -p 8182:8182 -p 8282:8282 -p 9090:9090 -p 9999:9999 \ -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ - -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ - -i edc-controlplane-memory:latest + -i edc-runtime-memory:latest ``` diff --git a/edc-controlplane/edc-controlplane-memory/build.gradle.kts b/edc-controlplane/edc-runtime-memory/build.gradle.kts similarity index 68% rename from edc-controlplane/edc-controlplane-memory/build.gradle.kts rename to edc-controlplane/edc-runtime-memory/build.gradle.kts index 1254a3634..372ec486d 100644 --- a/edc-controlplane/edc-controlplane-memory/build.gradle.kts +++ b/edc-controlplane/edc-runtime-memory/build.gradle.kts @@ -1,17 +1,16 @@ -import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage - plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { - runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) + implementation(edc.spi.core) + runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) { + exclude(module = "data-encryption") + } + runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) runtimeOnly(edc.core.controlplane) - runtimeOnly(edc.azure.vault) - runtimeOnly(edc.azure.identity) - } tasks.withType { diff --git a/edc-controlplane/edc-runtime-memory/notice.md b/edc-controlplane/edc-runtime-memory/notice.md new file mode 100644 index 000000000..f33bb6885 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/notice.md @@ -0,0 +1,28 @@ +# Notice for Docker image + +An EDC Control Plane using memory-based storage, and Azure KeyVault as secret store. + +DockerHub: https://hub.docker.com/r/tractusx/edc-runtime-memory + +Eclipse Tractus-X product(s) installed within the image: + +## TractusX-EDC Control Plane + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) + +## Used base image + +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. diff --git a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile b/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile similarity index 59% rename from edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile rename to edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile index b3e04fac7..de17857c4 100644 --- a/edc-controlplane/edc-controlplane-memory/src/main/docker/Dockerfile +++ b/edc-controlplane/edc-runtime-memory/src/main/docker/Dockerfile @@ -1,4 +1,5 @@ # +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # Copyright (c) 2023 ZF Friedrichshafen AG # Copyright (c) 2022,2023 Mercedes-Benz Tech Innovation GmbH # Copyright (c) 2021,2023 Contributors to the Eclipse Foundation @@ -18,15 +19,8 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel -ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" - -HEALTHCHECK NONE - -RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar - -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker @@ -46,18 +40,10 @@ RUN adduser \ USER "$APP_USER" WORKDIR /app -COPY --from=otel /tmp/opentelemetry-javaagent.jar . COPY ${JAR} edc-controlplane.jar HEALTHCHECK NONE -CMD ["java", \ - "-javaagent:/app/opentelemetry-javaagent.jar", \ - "-Dedc.fs.config=/app/configuration.properties", \ - "-Djava.util.logging.config.file=/app/logging.properties", \ - "-Dotel.javaagent.configuration-file=/app/opentelemetry.properties", \ - "-Dotel.metrics.exporter=prometheus", \ - "-Dotel.exporter.prometheus.port=9090", \ - "-Djava.security.egd=file:/dev/urandom", \ - "-jar", \ - "edc-controlplane.jar"] +# need the sh -c syntax so that the SECRETS variable gets expanded +# use the "exec" syntax so that SIGINT reaches the JVM -> graceful termination +CMD ["sh", "-c", "exec java -Dedc.fs.config=/app/configuration.properties -Dedc.vault.secrets=\"${SECRETS}\" -Djava.util.logging.config.file=/app/logging.properties -Djava.security.egd=file:/dev/urandom -jar edc-controlplane.jar"] diff --git a/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVault.java b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVault.java new file mode 100644 index 000000000..9b92a83c0 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVault.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.vault.memory; + +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.security.Vault; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class InMemoryVault implements Vault { + private final Map secrets = new ConcurrentHashMap<>(); + private final Monitor monitor; + + public InMemoryVault(Monitor monitor) { + this.monitor = monitor; + } + + @Override + public @Nullable String resolveSecret(String s) { + monitor.debug("resolving secret " + s); + return secrets.getOrDefault(s, null); + } + + @Override + public Result storeSecret(String s, String s1) { + monitor.debug("storing secret " + s); + secrets.put(s, s1); + return Result.success(); + } + + @Override + public Result deleteSecret(String s) { + monitor.debug("deleting secret " + s); + return secrets.remove(s) == null ? + Result.failure("Secret with key " + s + " does not exist") : + Result.success(); + } +} diff --git a/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java new file mode 100644 index 000000000..c8d2cc7d2 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/main/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtension.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ +package org.eclipse.tractusx.edc.vault.memory; + +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.runtime.metamodel.annotation.Provides; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.spi.security.*; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; + +import java.util.stream.Stream; + +@Provides({PrivateKeyResolver.class, CertificateResolver.class}) +@Extension(value = "In-memory vault extension", categories = {"vault", "security"}) +public class VaultMemoryExtension implements ServiceExtension { + + @Setting(value = "Secrets with which the vault gets initially populated. Specify as comma-separated list of key:secret pairs.") + public static final String VAULT_MEMORY_SECRETS_PROPERTY = "edc.vault.secrets"; + public static final String NAME = "In-Memory Vault Extension"; + + @Override + public String name() { + return NAME; + } + + @Provider + public Vault createInMemVault(ServiceExtensionContext context) { + var seedSecrets = context.getSetting(VAULT_MEMORY_SECRETS_PROPERTY, null); + var vault = new InMemoryVault(context.getMonitor()); + context.registerService(PrivateKeyResolver.class, new VaultPrivateKeyResolver(vault)); + context.registerService(CertificateResolver.class, new VaultCertificateResolver(vault)); + if (seedSecrets != null) { + Stream.of(seedSecrets.split(";")) + .filter(pair -> pair.contains(":")) + .map(kvp -> kvp.split(":", 2)) + .filter(kvp -> kvp.length >= 2) + .forEach(pair -> vault.storeSecret(pair[0], pair[1])); + } + return vault; + } +} diff --git a/charts/edc-controlplane/templates/configmap-env.yaml b/edc-controlplane/edc-runtime-memory/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 62% rename from charts/edc-controlplane/templates/configmap-env.yaml rename to edc-controlplane/edc-runtime-memory/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index d33071a58..b105388ea 100644 --- a/charts/edc-controlplane/templates/configmap-env.yaml +++ b/edc-controlplane/edc-runtime-memory/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -1,8 +1,6 @@ # -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# Copyright (c) 2021,2022 Contributors to the Eclipse Foundation # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -20,13 +18,4 @@ # SPDX-License-Identifier: Apache-2.0 # ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "edc-controlplane.fullname" . }}-env - namespace: {{ .Release.Namespace | default "default" | quote }} - labels: - {{- include "edc-controlplane.labels" . | nindent 4 }} -data: - {{- toYaml .Values.env | nindent 2 }} +org.eclipse.tractusx.edc.vault.memory.VaultMemoryExtension diff --git a/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVaultTest.java b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVaultTest.java new file mode 100644 index 000000000..c00ae8180 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/InMemoryVaultTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.vault.memory; + +import org.eclipse.edc.spi.monitor.Monitor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class InMemoryVaultTest { + + private InMemoryVault vault; + + @BeforeEach + void setUp() { + vault = new InMemoryVault(mock(Monitor.class)); + } + + @Test + void resolveSecret() { + assertThat(vault.resolveSecret("key")).isNull(); + vault.storeSecret("key", "secret"); + assertThat(vault.resolveSecret("key")).isEqualTo("secret"); + } + + @Test + void storeSecret() { + assertThat(vault.storeSecret("key", "value1").succeeded()).isTrue(); + assertThat(vault.resolveSecret("key")).isEqualTo("value1"); + assertThat(vault.storeSecret("key", "value2").succeeded()).isTrue(); + assertThat(vault.resolveSecret("key")).isEqualTo("value2"); + } + + @Test + void deleteSecret() { + assertThat(vault.deleteSecret("key").succeeded()).isFalse(); + assertThat(vault.storeSecret("key", "value1").succeeded()).isTrue(); + assertThat(vault.deleteSecret("key").succeeded()).isTrue(); + assertThat(vault.resolveSecret("key")).isNull(); + + } +} diff --git a/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java new file mode 100644 index 000000000..bec589d79 --- /dev/null +++ b/edc-controlplane/edc-runtime-memory/src/test/java/org/eclipse/tractusx/edc/vault/memory/VaultMemoryExtensionTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.vault.memory; + +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +class VaultMemoryExtensionTest { + private VaultMemoryExtension extension; + private ServiceExtensionContext context; + private Monitor monitor; + + @BeforeEach + void setup() { + extension = new VaultMemoryExtension(); + context = mock(ServiceExtensionContext.class); + monitor = mock(Monitor.class); + when(context.getMonitor()).thenReturn(monitor); + } + + @Test + void name() { + assertThat(extension.name()).isEqualTo("In-Memory Vault Extension"); + } + + @ParameterizedTest + @ValueSource(strings = {"key1:", "key1:value1", "key1:value1;", ";key1:value1", ";sdf;key1:value1"}) + void createInMemVault_validString(String secret) { + when(context.getSetting(eq(VaultMemoryExtension.VAULT_MEMORY_SECRETS_PROPERTY), eq(null))).thenReturn(secret); + extension.createInMemVault(context); + verify(monitor, times(1)).debug(anyString()); + } +} diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index bde51831c..02d29b7db 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -2,14 +2,14 @@ plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) implementation(edc.azure.vault) implementation(edc.azure.identity) - implementation("com.azure:azure-security-keyvault-secrets:4.5.4") + implementation("com.azure:azure-security-keyvault-secrets:4.6.0") } tasks.withType { diff --git a/edc-dataplane/edc-dataplane-azure-vault/notice.md b/edc-dataplane/edc-dataplane-azure-vault/notice.md new file mode 100644 index 000000000..065833ed2 --- /dev/null +++ b/edc-dataplane/edc-dataplane-azure-vault/notice.md @@ -0,0 +1,28 @@ +# Notice for Docker image + +An EDC Data Plane using the Azure KeyVault. + +DockerHub: https://hub.docker.com/r/tractusx/edc-dataplane-azure-vault + +Eclipse Tractus-X product(s) installed within the image: + +## TractusX-EDC Data Plane + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) + +## Used base image + +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. diff --git a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile index 5c3b12f11..cb3e3f817 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-azure-vault/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-dataplane/edc-dataplane-base/build.gradle.kts b/edc-dataplane/edc-dataplane-base/build.gradle.kts index 686e5fd06..cc873dcea 100644 --- a/edc-dataplane/edc-dataplane-base/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-base/build.gradle.kts @@ -4,18 +4,18 @@ plugins { } dependencies { - implementation(edc.config.filesystem) - implementation(edc.dpf.awss3) - implementation(edc.dpf.oauth2) - implementation(edc.dpf.http) + runtimeOnly(project(":edc-extensions:observability-api-customization")) - implementation(edc.dpf.framework) - implementation(edc.dpf.api) - implementation(edc.api.observability) - implementation(edc.core.connector) - implementation(edc.boot) + runtimeOnly(edc.config.filesystem) + runtimeOnly(edc.dpf.awss3) + runtimeOnly(edc.dpf.oauth2) + runtimeOnly(edc.dpf.http) + runtimeOnly(edc.dpf.framework) + runtimeOnly(edc.dpf.api) + runtimeOnly(edc.core.connector) + runtimeOnly(edc.boot) - implementation(edc.bundles.monitoring) - implementation(edc.ext.http) + runtimeOnly(edc.bundles.monitoring) + runtimeOnly(edc.ext.http) } \ No newline at end of file diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts index 7f53f8c5c..0ae98dd2d 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/build.gradle.kts @@ -2,7 +2,7 @@ plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md new file mode 100644 index 000000000..054c5e35f --- /dev/null +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/notice.md @@ -0,0 +1,28 @@ +# Notice for Docker image + +An EDC Data Plane using the HashiCorp Vault + +DockerHub: https://hub.docker.com/r/tractusx/edc-dataplane-hashicorp-vault + +Eclipse Tractus-X product(s) installed within the image: + +## TractusX-EDC Data Plane + +- GitHub: https://github.com/eclipse-tractusx/tractusx-edc +- Project home: https://projects.eclipse.org/projects/automotive.tractusx +- Dockerfile: https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +- Project license: [Apache License, Version 2.0](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE) + +## Used base image + +- [eclipse-temurin:17.0.6_10-jre-alpine](https://github.com/adoptium/containers) +- Official Eclipse Temurin DockerHub page: https://hub.docker.com/_/eclipse-temurin +- Eclipse Temurin Project: https://projects.eclipse.org/projects/adoptium.temurin +- Additional information about the Eclipse Temurin + images: https://github.com/docker-library/repo-info/tree/master/repos/eclipse-temurin + +As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc +from the base distribution, along with any direct or indirect dependencies of the primary software being contained). + +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies +with any relevant licenses for all software contained within. diff --git a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile index 5c3b12f11..cb3e3f817 100644 --- a/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile +++ b/edc-dataplane/edc-dataplane-hashicorp-vault/src/main/docker/Dockerfile @@ -18,7 +18,7 @@ # # SPDX-License-Identifier: Apache-2.0 # -FROM alpine:3.17.2 as otel +FROM alpine:3.17.3 as otel ENV OTEL_AGENT_LOCATION "https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.12.1/opentelemetry-javaagent.jar" @@ -26,7 +26,7 @@ HEALTHCHECK NONE RUN wget ${OTEL_AGENT_LOCATION} -O /tmp/opentelemetry-javaagent.jar -FROM eclipse-temurin:11.0.18_10-jre-alpine +FROM eclipse-temurin:17.0.6_10-jre-alpine ARG JAR ARG APP_USER=docker diff --git a/edc-extensions/business-partner-validation/build.gradle.kts b/edc-extensions/business-partner-validation/build.gradle.kts index 53cb11e31..198886d9a 100644 --- a/edc-extensions/business-partner-validation/build.gradle.kts +++ b/edc-extensions/business-partner-validation/build.gradle.kts @@ -1,4 +1,3 @@ - plugins { `java-library` `maven-publish` @@ -7,5 +6,6 @@ plugins { dependencies { api(edc.spi.core) implementation(edc.spi.policy) + implementation(edc.spi.contract) implementation(edc.spi.policyengine) } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java index ee076406f..d88293a72 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -26,6 +26,7 @@ import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.policy.model.Prohibition; import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; @@ -37,60 +38,73 @@ public class BusinessPartnerValidationExtension implements ServiceExtension { - /** - * The key for business partner numbers constraints. Must be used as left operand when declaring - * constraints. - * - *

Example: - * - *

-   * {
-   *     "constraint": {
-   *         "leftOperand": "BusinessPartnerNumber",
-   *         "operator": "EQ",
-   *         "rightOperand": "BPNLCDQ90000X42KU"
-   *     }
-   * }
-   * 
- */ - public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; - - public BusinessPartnerValidationExtension() {} - - public BusinessPartnerValidationExtension( - final RuleBindingRegistry ruleBindingRegistry, final PolicyEngine policyEngine) { - this.ruleBindingRegistry = ruleBindingRegistry; - this.policyEngine = policyEngine; - } - - @Inject private RuleBindingRegistry ruleBindingRegistry; - - @Inject private PolicyEngine policyEngine; - - @Override - public String name() { - return "Business Partner Validation Extension"; - } - - @Override - public void initialize(ServiceExtensionContext context) { - - final Monitor monitor = context.getMonitor(); - - final BusinessPartnerDutyFunction dutyFunction = new BusinessPartnerDutyFunction(monitor); - final BusinessPartnerPermissionFunction permissionFunction = - new BusinessPartnerPermissionFunction(monitor); - final BusinessPartnerProhibitionFunction prohibitionFunction = - new BusinessPartnerProhibitionFunction(monitor); - - ruleBindingRegistry.bind("USE", ALL_SCOPES); - ruleBindingRegistry.bind(BUSINESS_PARTNER_CONSTRAINT_KEY, ALL_SCOPES); - - policyEngine.registerFunction( - ALL_SCOPES, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); - policyEngine.registerFunction( - ALL_SCOPES, Permission.class, BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); - policyEngine.registerFunction( - ALL_SCOPES, Prohibition.class, BUSINESS_PARTNER_CONSTRAINT_KEY, prohibitionFunction); - } + /** + * The key for business partner numbers constraints. Must be used as left operand when declaring + * constraints. + * + *

Example: + * + *

+     * {
+     *     "constraint": {
+     *         "leftOperand": "BusinessPartnerNumber",
+     *         "operator": "EQ",
+     *         "rightOperand": "BPNLCDQ90000X42KU"
+     *     }
+     * }
+     * 
+ */ + public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; + + public static final String DEFAULT_LOG_AGREEMENT_EVALUATION = "true"; + + + @Setting(value = "Enable logging when evaluating the business partner constraints in the agreement validation", type = "boolean", defaultValue = DEFAULT_LOG_AGREEMENT_EVALUATION) + public static final String BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION = "tractusx.businesspartnervalidation.log.agreement.validation"; + @Inject + private RuleBindingRegistry ruleBindingRegistry; + @Inject + private PolicyEngine policyEngine; + + public BusinessPartnerValidationExtension() { + } + + public BusinessPartnerValidationExtension( + final RuleBindingRegistry ruleBindingRegistry, final PolicyEngine policyEngine) { + this.ruleBindingRegistry = ruleBindingRegistry; + this.policyEngine = policyEngine; + } + + @Override + public String name() { + return "Business Partner Validation Extension"; + } + + @Override + public void initialize(ServiceExtensionContext context) { + + final Monitor monitor = context.getMonitor(); + + var logAgreementEvaluation = logAgreementEvaluationSetting(context); + + final BusinessPartnerDutyFunction dutyFunction = new BusinessPartnerDutyFunction(monitor, logAgreementEvaluation); + final BusinessPartnerPermissionFunction permissionFunction = + new BusinessPartnerPermissionFunction(monitor, logAgreementEvaluation); + final BusinessPartnerProhibitionFunction prohibitionFunction = + new BusinessPartnerProhibitionFunction(monitor, logAgreementEvaluation); + + ruleBindingRegistry.bind("USE", ALL_SCOPES); + ruleBindingRegistry.bind(BUSINESS_PARTNER_CONSTRAINT_KEY, ALL_SCOPES); + + policyEngine.registerFunction( + ALL_SCOPES, Duty.class, BUSINESS_PARTNER_CONSTRAINT_KEY, dutyFunction); + policyEngine.registerFunction( + ALL_SCOPES, Permission.class, BUSINESS_PARTNER_CONSTRAINT_KEY, permissionFunction); + policyEngine.registerFunction( + ALL_SCOPES, Prohibition.class, BUSINESS_PARTNER_CONSTRAINT_KEY, prohibitionFunction); + } + + private Boolean logAgreementEvaluationSetting(ServiceExtensionContext context) { + return Boolean.parseBoolean(context.getSetting(BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION, DEFAULT_LOG_AGREEMENT_EVALUATION)); + } } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java index 55cb0d52b..ecb5b81ef 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidation.java @@ -20,132 +20,147 @@ package org.eclipse.tractusx.edc.validation.businesspartner.functions; -import java.util.Map; -import java.util.Objects; +import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreement; import org.eclipse.edc.policy.engine.spi.PolicyContext; import org.eclipse.edc.policy.model.Operator; import org.eclipse.edc.spi.agent.ParticipantAgent; import org.eclipse.edc.spi.monitor.Monitor; +import java.util.Map; +import java.util.Objects; + +import static java.lang.String.format; + /** * Abstract class for BusinessPartnerNumber validation. This class may be inherited from the EDC * policy enforcing functions for duties, permissions and prohibitions. */ public abstract class AbstractBusinessPartnerValidation { - // Developer Note: - // Problems reported to the policy context are not logged. Therefore, everything - // that is reported to the policy context should be logged, too. - - private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING = - "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'EQ' right value must be of type 'String'. Unsupported type: '%s'"; - private static final String FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR = - "Failing evaluation because of invalid BusinessPartnerNumber constraint. As operator only 'EQ' is supported. Unsupported operator: '%s'"; - - private final Monitor monitor; - - protected AbstractBusinessPartnerValidation(Monitor monitor) { - this.monitor = Objects.requireNonNull(monitor); - } - - /** - * Name of the claim that contains the Business Partner Number. - * - *

Please note: At the time of writing (April 2022) the business partner - * number is part of the 'referringConnector' claim in the IDS DAT token. This will probably - * change for the next release. - */ - private static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; - - /** - * Evaluation funtion to decide whether a claim belongs to a specific business partner. - * - * @param operator operator of the constraint - * @param rightValue right value fo the constraint, that contains the business partner number - * (e.g. BPNLCDQ90000X42KU) - * @param policyContext context of the policy with claims - * @return true if claims are from the constrained business partner - */ - protected boolean evaluate( - final Operator operator, final Object rightValue, final PolicyContext policyContext) { - - if (policyContext.hasProblems() && !policyContext.getProblems().isEmpty()) { - String problems = String.join(", ", policyContext.getProblems()); - String message = - String.format( - "BusinessPartnerNumberValidation: Rejecting PolicyContext with problems. Problems: %s", - problems); - monitor.debug(message); - return false; + // Developer Note: + // Problems reported to the policy context are not logged. Therefore, everything + // that is reported to the policy context should be logged, too. + + private static final String FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. For operator 'EQ' right value must be of type 'String'. Unsupported type: '%s'"; + private static final String FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR = + "Failing evaluation because of invalid BusinessPartnerNumber constraint. As operator only 'EQ' is supported. Unsupported operator: '%s'"; + /** + * Name of the claim that contains the Business Partner Number. + * + *

Please note: At the time of writing (April 2022) the business partner + * number is part of the 'referringConnector' claim in the IDS DAT token. This will probably + * change for the next release. + */ + private static final String REFERRING_CONNECTOR_CLAIM = "referringConnector"; + private final Monitor monitor; + private final boolean logAgreementEvaluation; + + protected AbstractBusinessPartnerValidation(Monitor monitor, boolean logAgreementEvaluation) { + this.monitor = Objects.requireNonNull(monitor); + this.logAgreementEvaluation = logAgreementEvaluation; } - final ParticipantAgent participantAgent = policyContext.getParticipantAgent(); - final Map claims = participantAgent.getClaims(); - - if (!claims.containsKey(REFERRING_CONNECTOR_CLAIM)) { - return false; + /** + * At the time of writing (11. April 2022) the business partner number is part of the + * 'referringConnector' claim, which contains a connector URL. As the CX projects are not further + * aligned about the URL formatting, the enforcement can only be done by checking whether the URL + * _contains_ the number. As this introduces some insecurities when validation business partner + * numbers, this should be addresses in the long term. + * + * @param referringConnectorClaim describing URL with business partner number + * @param businessPartnerNumber of the constraint + * @return true if claim contains the business partner number + */ + private static boolean isCorrectBusinessPartner( + String referringConnectorClaim, String businessPartnerNumber) { + return referringConnectorClaim.contains(businessPartnerNumber); } - Object referringConnectorClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); - String referringConnectorClaim = null; - - if (referringConnectorClaimObject instanceof String) { - referringConnectorClaim = (String) referringConnectorClaimObject; + public boolean isLogAgreementEvaluation() { + return logAgreementEvaluation; } - if (referringConnectorClaim == null || referringConnectorClaim.isEmpty()) { - return false; + /** + * Evaluation funtion to decide whether a claim belongs to a specific business partner. + * + * @param operator operator of the constraint + * @param rightValue right value fo the constraint, that contains the business partner number + * (e.g. BPNLCDQ90000X42KU) + * @param policyContext context of the policy with claims + * @return true if claims are from the constrained business partner + */ + protected boolean evaluate( + final Operator operator, final Object rightValue, final PolicyContext policyContext) { + + if (policyContext.hasProblems() && !policyContext.getProblems().isEmpty()) { + String problems = String.join(", ", policyContext.getProblems()); + String message = + format( + "BusinessPartnerNumberValidation: Rejecting PolicyContext with problems. Problems: %s", + problems); + monitor.debug(message); + return false; + } + + final ParticipantAgent participantAgent = policyContext.getParticipantAgent(); + final Map claims = participantAgent.getClaims(); + + if (!claims.containsKey(REFERRING_CONNECTOR_CLAIM)) { + return false; + } + + Object referringConnectorClaimObject = claims.get(REFERRING_CONNECTOR_CLAIM); + String referringConnectorClaim = null; + + if (referringConnectorClaimObject instanceof String) { + referringConnectorClaim = (String) referringConnectorClaimObject; + } + + if (referringConnectorClaim == null || referringConnectorClaim.isEmpty()) { + return false; + } + + if (operator == Operator.EQ) { + return isBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); + } else { + final String message = format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR, operator); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } } - if (operator == Operator.EQ) { - return isBusinessPartnerNumber(referringConnectorClaim, rightValue, policyContext); - } else { - final String message = String.format(FAIL_EVALUATION_BECAUSE_UNSUPPORTED_OPERATOR, operator); - monitor.warning(message); - policyContext.reportProblem(message); - return false; - } - } - - /** - * @param referringConnectorClaim of the participant - * @param businessPartnerNumber object - * @return true if object is string and successfully evaluated against the claim - */ - private boolean isBusinessPartnerNumber( - String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { - if (businessPartnerNumber == null) { - final String message = String.format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); - monitor.warning(message); - policyContext.reportProblem(message); - return false; + /** + * @param referringConnectorClaim of the participant + * @param businessPartnerNumber object + * @return true if object is string and successfully evaluated against the claim + */ + private boolean isBusinessPartnerNumber( + String referringConnectorClaim, Object businessPartnerNumber, PolicyContext policyContext) { + if (businessPartnerNumber == null) { + final String message = format(FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, "null"); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } + if (!(businessPartnerNumber instanceof String)) { + final String message = + format( + FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, + businessPartnerNumber.getClass().getName()); + monitor.warning(message); + policyContext.reportProblem(message); + return false; + } + + var businessPartnerNumberStr = (String) businessPartnerNumber; + var agreement = policyContext.getContextData(ContractAgreement.class); + var isCorrectBusinessPartner = isCorrectBusinessPartner(referringConnectorClaim, businessPartnerNumberStr); + + if (agreement != null && logAgreementEvaluation) { + monitor.info(format("Evaluated policy access for referringConnectorClaim: %s and contract id: %s with result: %s", referringConnectorClaim, agreement.getId(), isCorrectBusinessPartner)); + } + return isCorrectBusinessPartner; } - if (!(businessPartnerNumber instanceof String)) { - final String message = - String.format( - FAIL_EVALUATION_BECAUSE_RIGHT_VALUE_NOT_STRING, - businessPartnerNumber.getClass().getName()); - monitor.warning(message); - policyContext.reportProblem(message); - return false; - } - - return isCorrectBusinessPartner(referringConnectorClaim, (String) businessPartnerNumber); - } - - /** - * At the time of writing (11. April 2022) the business partner number is part of the - * 'referringConnector' claim, which contains a connector URL. As the CX projects are not further - * aligned about the URL formatting, the enforcement can only be done by checking whether the URL - * _contains_ the number. As this introduces some insecurities when validation business partner - * numbers, this should be addresses in the long term. - * - * @param referringConnectorClaim describing URL with business partner number - * @param businessPartnerNumber of the constraint - * @return true if claim contains the business partner number - */ - private static boolean isCorrectBusinessPartner( - String referringConnectorClaim, String businessPartnerNumber) { - return referringConnectorClaim.contains(businessPartnerNumber); - } } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java index f53ba3cbc..061d7fd7d 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerDutyFunction.java @@ -26,16 +26,18 @@ import org.eclipse.edc.policy.model.Operator; import org.eclipse.edc.spi.monitor.Monitor; -/** AtomicConstraintFunction to validate business partner numbers for edc duties. */ +/** + * AtomicConstraintFunction to validate business partner numbers for edc duties. + */ public class BusinessPartnerDutyFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { + implements AtomicConstraintFunction { - public BusinessPartnerDutyFunction(Monitor monitor) { - super(monitor); - } + public BusinessPartnerDutyFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { + super(monitor, shouldLogOnAgreementEvaluation); + } - @Override - public boolean evaluate(Operator operator, Object rightValue, Duty rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } + @Override + public boolean evaluate(Operator operator, Object rightValue, Duty rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java index 07bda765e..b6713c477 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerPermissionFunction.java @@ -26,17 +26,19 @@ import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.spi.monitor.Monitor; -/** AtomicConstraintFunction to validate business partner numbers for edc permissions. */ +/** + * AtomicConstraintFunction to validate business partner numbers for edc permissions. + */ public class BusinessPartnerPermissionFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { + implements AtomicConstraintFunction { - public BusinessPartnerPermissionFunction(Monitor monitor) { - super(monitor); - } + public BusinessPartnerPermissionFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { + super(monitor, shouldLogOnAgreementEvaluation); + } - @Override - public boolean evaluate( - Operator operator, Object rightValue, Permission rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } + @Override + public boolean evaluate( + Operator operator, Object rightValue, Permission rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } } diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java index f3cddf9fe..79e318741 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/BusinessPartnerProhibitionFunction.java @@ -26,17 +26,19 @@ import org.eclipse.edc.policy.model.Prohibition; import org.eclipse.edc.spi.monitor.Monitor; -/** AtomicConstraintFunction to validate business partner numbers for edc prohibitions. */ +/** + * AtomicConstraintFunction to validate business partner numbers for edc prohibitions. + */ public class BusinessPartnerProhibitionFunction extends AbstractBusinessPartnerValidation - implements AtomicConstraintFunction { + implements AtomicConstraintFunction { - public BusinessPartnerProhibitionFunction(Monitor monitor) { - super(monitor); - } + public BusinessPartnerProhibitionFunction(Monitor monitor, boolean shouldLogOnAgreementEvaluation) { + super(monitor, shouldLogOnAgreementEvaluation); + } - @Override - public boolean evaluate( - Operator operator, Object rightValue, Prohibition rule, PolicyContext context) { - return evaluate(operator, rightValue, context); - } + @Override + public boolean evaluate( + Operator operator, Object rightValue, Prohibition rule, PolicyContext context) { + return evaluate(operator, rightValue, context); + } } diff --git a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java index 0240dc9ef..dcea3be41 100644 --- a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtensionTest.java @@ -27,10 +27,13 @@ import org.eclipse.edc.policy.model.Prohibition; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerPermissionFunction; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -105,4 +108,24 @@ void testRegisterProhibitionFunction() { eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), any()); } + + @Test + void testLogConfiguration() { + + when(serviceExtensionContext.getSetting(BusinessPartnerValidationExtension.BUSINESS_PARTNER_VALIDATION_LOG_AGREEMENT_VALIDATION, "true")).thenReturn("false"); + + var captor = ArgumentCaptor.forClass(BusinessPartnerPermissionFunction.class); + // invoke + extension.initialize(serviceExtensionContext); + + // verify + verify(policyEngine) + .registerFunction( + anyString(), + eq(Permission.class), + eq(BusinessPartnerValidationExtension.BUSINESS_PARTNER_CONSTRAINT_KEY), + captor.capture()); + + assertThat(captor.getValue().isLogAgreementEvaluation()).isFalse(); + } } diff --git a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java index e8909c04e..2bc0738b0 100644 --- a/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java +++ b/edc-extensions/business-partner-validation/src/test/java/org/eclipse/tractusx/edc/validation/businesspartner/functions/AbstractBusinessPartnerValidationTest.java @@ -20,10 +20,10 @@ package org.eclipse.tractusx.edc.validation.businesspartner.functions; -import java.util.Collections; -import java.util.List; +import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreement; import org.eclipse.edc.policy.engine.spi.PolicyContext; import org.eclipse.edc.policy.model.Operator; +import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.agent.ParticipantAgent; import org.eclipse.edc.spi.monitor.Monitor; import org.junit.jupiter.api.Assertions; @@ -31,143 +31,180 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; + class AbstractBusinessPartnerValidationTest { - private AbstractBusinessPartnerValidation validation; + private AbstractBusinessPartnerValidation validation; + + // mocks + private Monitor monitor; + private PolicyContext policyContext; + private ParticipantAgent participantAgent; + + @BeforeEach + void BeforeEach() { + this.monitor = Mockito.mock(Monitor.class); + this.policyContext = Mockito.mock(PolicyContext.class); + this.participantAgent = Mockito.mock(ParticipantAgent.class); + + Mockito.when(policyContext.getParticipantAgent()).thenReturn(participantAgent); + + validation = new AbstractBusinessPartnerValidation(monitor, true) { + }; + } + + @ParameterizedTest + @EnumSource(Operator.class) + void testFailsOnUnsupportedOperations(Operator operator) { - // mocks - private Monitor monitor; - private PolicyContext policyContext; - private ParticipantAgent participantAgent; + if (operator == Operator.EQ) { // only allowed operator + return; + } - @BeforeEach - void BeforeEach() { - this.monitor = Mockito.mock(Monitor.class); - this.policyContext = Mockito.mock(PolicyContext.class); - this.participantAgent = Mockito.mock(ParticipantAgent.class); + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("yes"); - Mockito.when(policyContext.getParticipantAgent()).thenReturn(participantAgent); + // invoke & assert + Assertions.assertFalse(validation.evaluate(operator, "foo", policyContext)); + } + + @Test + void testFailsOnUnsupportedRightValue() { + + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("yes"); + + // invoke & assert + Assertions.assertFalse(validation.evaluate(Operator.EQ, 1, policyContext)); + } + + @Test + void testValidationFailsWhenClaimMissing() { - validation = new AbstractBusinessPartnerValidation(monitor) {}; - } + // prepare + prepareContextProblems(null); - @ParameterizedTest - @EnumSource(Operator.class) - void testFailsOnUnsupportedOperations(Operator operator) { + // invoke + final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); - if (operator == Operator.EQ) { // only allowed operator - return; + // assert + Assertions.assertFalse(isValid); } - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("yes"); + @Test + void testValidationSucceedsWhenClaimContainsValue() { - // invoke & assert - Assertions.assertFalse(validation.evaluate(operator, "foo", policyContext)); - } + // prepare + prepareContextProblems(null); - @Test - void testFailsOnUnsupportedRightValue() { + // prepare equals + prepareBusinessPartnerClaim("foo"); + final boolean isEqualsTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("yes"); + // prepare contains + prepareBusinessPartnerClaim("foobar"); + final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - // invoke & assert - Assertions.assertFalse(validation.evaluate(Operator.EQ, 1, policyContext)); - } + // assert + Assertions.assertTrue(isEqualsTrue); + Assertions.assertTrue(isContainedTrue); + } - @Test - void testValidationFailsWhenClaimMissing() { + @Test + void testValidationWhenParticipantHasProblems() { - // prepare - prepareContextProblems(null); + // prepare + prepareContextProblems(Collections.singletonList("big problem")); + prepareBusinessPartnerClaim("foo"); - // invoke - final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); + // invoke + final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); - // assert - Assertions.assertFalse(isValid); - } + // Mockito.verify(monitor.debug(Mockito.anyString()); + Assertions.assertFalse(isValid); + } - @Test - void testValidationSucceedsWhenClaimContainsValue() { + @Test + void testValidationWhenSingleParticipantIsValid() { - // prepare - prepareContextProblems(null); + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("foo"); - // prepare equals - prepareBusinessPartnerClaim("foo"); - final boolean isEqualsTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + // invoke + final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - // prepare contains - prepareBusinessPartnerClaim("foobar"); - final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + // Mockito.verify(monitor.debug(Mockito.anyString()); + Assertions.assertTrue(isContainedTrue); + } - // assert - Assertions.assertTrue(isEqualsTrue); - Assertions.assertTrue(isContainedTrue); - } + @Test + void testValidationWhenSingleParticipantIsValidWithAgreement() { - @Test - void testValidationWhenParticipantHasProblems() { + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("foo"); - // prepare - prepareContextProblems(Collections.singletonList("big problem")); - prepareBusinessPartnerClaim("foo"); + var captor = ArgumentCaptor.forClass(String.class); - // invoke - final boolean isValid = validation.evaluate(Operator.EQ, "foo", policyContext); + var agreement = ContractAgreement.Builder.newInstance() + .id("agreementId") + .providerAgentId("provider") + .consumerAgentId("consumer") + .assetId("assetId") + .policy(Policy.Builder.newInstance().build()) + .build(); - // Mockito.verify(monitor.debug(Mockito.anyString()); - Assertions.assertFalse(isValid); - } + Mockito.when(policyContext.getContextData(eq(ContractAgreement.class))).thenReturn(agreement); - @Test - void testValidationWhenSingleParticipantIsValid() { + // invoke + final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("foo"); + Assertions.assertTrue(isContainedTrue); - // invoke - final boolean isContainedTrue = validation.evaluate(Operator.EQ, "foo", policyContext); + Mockito.verify(monitor).info(captor.capture()); - // Mockito.verify(monitor.debug(Mockito.anyString()); - Assertions.assertTrue(isContainedTrue); - } + assertThat(captor.getValue()).contains(agreement.getId()).contains("foo"); + } - // In the past it was possible to use the 'IN' constraint with multiple BPNs as - // a list. This is no longer supported. - // The EDC must now always decline this kind of BPN format. - @Test - void testValidationForMultipleParticipants() { + // In the past it was possible to use the 'IN' constraint with multiple BPNs as + // a list. This is no longer supported. + // The EDC must now always decline this kind of BPN format. + @Test + void testValidationForMultipleParticipants() { - // prepare - prepareContextProblems(null); - prepareBusinessPartnerClaim("foo"); + // prepare + prepareContextProblems(null); + prepareBusinessPartnerClaim("foo"); - // invoke & verify - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("foo", "bar"), policyContext)); - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of(1, "foo"), policyContext)); - Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("bar", "bar"), policyContext)); - } + // invoke & verify + Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("foo", "bar"), policyContext)); + Assertions.assertFalse(validation.evaluate(Operator.IN, List.of(1, "foo"), policyContext)); + Assertions.assertFalse(validation.evaluate(Operator.IN, List.of("bar", "bar"), policyContext)); + } - private void prepareContextProblems(List problems) { - Mockito.when(policyContext.getProblems()).thenReturn(problems); + private void prepareContextProblems(List problems) { + Mockito.when(policyContext.getProblems()).thenReturn(problems); - if (problems == null || problems.isEmpty()) { - Mockito.when(policyContext.hasProblems()).thenReturn(false); - } else { - Mockito.when(policyContext.hasProblems()).thenReturn(true); + if (problems == null || problems.isEmpty()) { + Mockito.when(policyContext.hasProblems()).thenReturn(false); + } else { + Mockito.when(policyContext.hasProblems()).thenReturn(true); + } } - } - private void prepareBusinessPartnerClaim(String businessPartnerNumber) { - Mockito.when(participantAgent.getClaims()) - .thenReturn(Collections.singletonMap("referringConnector", businessPartnerNumber)); - } + private void prepareBusinessPartnerClaim(String businessPartnerNumber) { + Mockito.when(participantAgent.getClaims()) + .thenReturn(Collections.singletonMap("referringConnector", businessPartnerNumber)); + } } diff --git a/edc-extensions/control-plane-adapter/README.md b/edc-extensions/control-plane-adapter/README.md index fe9d4787c..5f2d0d890 100644 --- a/edc-extensions/control-plane-adapter/README.md +++ b/edc-extensions/control-plane-adapter/README.md @@ -39,9 +39,17 @@ To run CP-Adapter in "PERSISTENT" mode, You need to create a proper tables with 1. Client sends a GET request with two parameters: assetId and the url of the provider controlplane: ```plain - /adapter/asset/sync/{assetId}?providerUrl={providerUrl} + {controlplaneUrl}:{web.http.management.port}/{web.http.management.path}/adapter/asset/sync/{assetId}?providerUrl={providerUrl} ``` + | Name | Description | + |----------------------------|----------------------------------------------------------------------------------| + | `controlplaneUrl` | The URL where the control plane of the consumer connector is available | + | `web.http.management.port` | Port of the management API provided by the control plane | + | `web.http.management.path` | Path of the management API provided by the control plane | + | `assetId` | ID of the wanted asset | + | `providerUrl` | URL pointing to the `data` endpoint of the IDS context of the provider connector | + The example ULR could be: ```plain diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java index 69c54a173..6f463fc82 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithm.java @@ -20,93 +20,91 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; +import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; +import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; +import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; +import org.eclipse.tractusx.edc.data.encryption.key.AesKey; +import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; + import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.Objects; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; -import lombok.NonNull; -import lombok.SneakyThrows; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.eclipse.tractusx.edc.data.encryption.algorithms.CryptoAlgorithm; -import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; -import org.eclipse.tractusx.edc.data.encryption.data.DecryptedData; -import org.eclipse.tractusx.edc.data.encryption.data.EncryptedData; -import org.eclipse.tractusx.edc.data.encryption.key.AesKey; -import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; -import org.jetbrains.annotations.NotNull; public class AesAlgorithm implements CryptoAlgorithm { - private static final String AES_GCM = "AES/GCM/NoPadding"; - private static final String AES = "AES"; - private static final Object MONITOR = new Object(); + private static final String AES_GCM = "AES/GCM/NoPadding"; + private static final String AES = "AES"; + private static final Object MONITOR = new Object(); + + private final SecureRandom secureRandom; - private final SecureRandom secureRandom; + private final CryptoDataFactory cryptoDataFactory; + private AesInitializationVectorIterator initializationVectorIterator; - @NonNull private final CryptoDataFactory cryptoDataFactory; - private AesInitializationVectorIterator initializationVectorIterator; + public AesAlgorithm(CryptoDataFactory cryptoDataFactory) { + this.cryptoDataFactory = Objects.requireNonNull(cryptoDataFactory); - @SneakyThrows - public AesAlgorithm(@NotNull CryptoDataFactory cryptoDataFactory) { - this.cryptoDataFactory = cryptoDataFactory; + // We use new SecureRandom() and not SecureRandom.getInstanceStrong(), as the second one + // would use a blocking algorithm, which leads to an increased encryption time of up to 3 + // minutes. Since we have already used /dev/urandom, which only provides pseudo-randomness and + // is also non-blocking, switching to a non-blocking algorithm should not matter here either. + this.secureRandom = new SecureRandom(); + this.initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); + } - // We use new SecureRandom() and not SecureRandom.getInstanceStrong(), as the second one - // would use a blocking algorithm, which leads to an increased encryption time of up to 3 - // minutes. Since we have already used /dev/urandom, which only provides pseudo-randomness and - // is also non-blocking, switching to a non-blocking algorithm should not matter here either. - this.secureRandom = new SecureRandom(); - this.initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); - } + @Override + public synchronized EncryptedData encrypt(DecryptedData data, AesKey key) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - @Override - public synchronized EncryptedData encrypt(DecryptedData data, AesKey key) - throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { + final byte[] initializationVector; + synchronized (MONITOR) { + if (!initializationVectorIterator.hasNext()) { + initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); + } - final byte[] initializationVector; - synchronized (MONITOR) { - if (!initializationVectorIterator.hasNext()) { - initializationVectorIterator = new AesInitializationVectorIterator(this.secureRandom); - } + initializationVector = initializationVectorIterator.next(); + } - initializationVector = initializationVectorIterator.next(); + Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); + final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); + final GCMParameterSpec gcmParameterSpec = + new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec); + byte[] encrypted = cipher.doFinal(data.getBytes()); + byte[] encryptedWithVector = ArrayUtil.concat(initializationVector, encrypted); + + return cryptoDataFactory.encryptedFromBytes(encryptedWithVector); } - Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); - final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); - final GCMParameterSpec gcmParameterSpec = - new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); - cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec); - byte[] encrypted = cipher.doFinal(data.getBytes()); - byte[] encryptedWithVector = ArrayUtil.concat(initializationVector, encrypted); - - return cryptoDataFactory.encryptedFromBytes(encryptedWithVector); - } - - @Override - public DecryptedData decrypt(EncryptedData data, AesKey key) - throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, - NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { - byte[] encryptedWithVector = data.getBytes(); - byte[] initializationVector = ArrayUtil.subArray(encryptedWithVector, 0, 16); - byte[] encrypted = ArrayUtil.subArray(encryptedWithVector, 16, encryptedWithVector.length - 16); - - Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); - final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); - final GCMParameterSpec gcmParameterSpec = - new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); - cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); - byte[] decryptedData = cipher.doFinal(encrypted); - return cryptoDataFactory.decryptedFromBytes(decryptedData); - } - - public String getAlgorithm() { - return this.secureRandom.getAlgorithm(); - } + @Override + public DecryptedData decrypt(EncryptedData data, AesKey key) + throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, + NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { + byte[] encryptedWithVector = data.getBytes(); + byte[] initializationVector = ArrayUtil.subArray(encryptedWithVector, 0, 16); + byte[] encrypted = ArrayUtil.subArray(encryptedWithVector, 16, encryptedWithVector.length - 16); + + Cipher cipher = Cipher.getInstance(AES_GCM, new BouncyCastleProvider()); + final SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), AES); + final GCMParameterSpec gcmParameterSpec = + new GCMParameterSpec(16 * 8 /* =128 */, initializationVector); + cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); + byte[] decryptedData = cipher.doFinal(encrypted); + return cryptoDataFactory.decryptedFromBytes(decryptedData); + } + + public String getAlgorithm() { + return this.secureRandom.getAlgorithm(); + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java index cd0a6b1ec..73d02c3d5 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIterator.java @@ -20,51 +20,50 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; +import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; + import java.security.SecureRandom; import java.util.Iterator; import java.util.NoSuchElementException; -import lombok.SneakyThrows; -import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; public class AesInitializationVectorIterator implements Iterator { - public static final int RANDOM_SIZE = 12; - public static final int COUNTER_SIZE = 4; - - private final ByteCounter counter; + public static final int RANDOM_SIZE = 12; + public static final int COUNTER_SIZE = 4; - private SecureRandom secureRandom; + private final ByteCounter counter; - public AesInitializationVectorIterator(SecureRandom secureRandom) { - this.counter = new ByteCounter(COUNTER_SIZE); - this.secureRandom = secureRandom; - } + private SecureRandom secureRandom; - public AesInitializationVectorIterator(ByteCounter byteCounter) { - this.counter = byteCounter; - } + public AesInitializationVectorIterator(SecureRandom secureRandom) { + this.counter = new ByteCounter(COUNTER_SIZE); + this.secureRandom = secureRandom; + } - @Override - public boolean hasNext() { - return !counter.isMaxed(); - } + public AesInitializationVectorIterator(ByteCounter byteCounter) { + this.counter = byteCounter; + } - @Override - public byte[] next() { - if (counter.isMaxed()) { - throw new NoSuchElementException(getClass().getSimpleName() + " has no more elements"); + @Override + public boolean hasNext() { + return !counter.isMaxed(); } - byte[] random = getNextRandom(); - counter.increment(); + @Override + public byte[] next() { + if (counter.isMaxed()) { + throw new NoSuchElementException(getClass().getSimpleName() + " has no more elements"); + } - return ArrayUtil.concat(random, counter.getBytes()); - } + byte[] random = getNextRandom(); + counter.increment(); - @SneakyThrows - public byte[] getNextRandom() { - byte[] newVector = new byte[RANDOM_SIZE]; - secureRandom.nextBytes(newVector); - return newVector; - } + return ArrayUtil.concat(random, counter.getBytes()); + } + + public byte[] getNextRandom() { + byte[] newVector = new byte[RANDOM_SIZE]; + secureRandom.nextBytes(newVector); + return newVector; + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java index 55eec8184..e7874c158 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/ByteCounter.java @@ -19,63 +19,69 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; -/** Big Endian Byte Counter */ +/** + * Big Endian Byte Counter + */ public class ByteCounter { - private final byte[] counter; - - /** - * Constructs a new ByteCounter with the given number of bytes. E.g. a ByteCounter with 4 bytes - * will have a counter value of [0, 0, 0, 0]. - * - * @param size number of bytes used by the counter - */ - public ByteCounter(int size) { - this.counter = new byte[size]; - } + private final byte[] counter; - /** - * Constructs a new ByteCounter with the given counter value. Counter cannot grow bigger than the - * size of the array. - * - * @param counter initial counter value - */ - public ByteCounter(byte[] counter) { - this.counter = counter; - } + /** + * Constructs a new ByteCounter with the given number of bytes. E.g. a ByteCounter with 4 bytes + * will have a counter value of [0, 0, 0, 0]. + * + * @param size number of bytes used by the counter + */ + public ByteCounter(int size) { + this.counter = new byte[size]; + } - /** Returns the counter value as a byte array. */ - public byte[] getBytes() { - return counter; - } + /** + * Constructs a new ByteCounter with the given counter value. Counter cannot grow bigger than the + * size of the array. + * + * @param counter initial counter value + */ + public ByteCounter(byte[] counter) { + this.counter = counter; + } - /** Returns true if counter is maxed */ - public boolean isMaxed() { - for (byte b : counter) { - if (b != (byte) 0xff) return false; + /** + * Returns the counter value as a byte array. + */ + public byte[] getBytes() { + return counter; } - return true; - } - /** - * Increments the counter by one. - * - * @throws IllegalStateException if the counter is already maxed - */ - public void increment() { - incrementByte(counter.length - 1); - } + /** + * Returns true if counter is maxed + */ + public boolean isMaxed() { + for (byte b : counter) { + if (b != (byte) 0xff) return false; + } + return true; + } - private void incrementByte(int index) { - if (isMaxed()) { - throw new IllegalStateException("Counter is already maxed"); + /** + * Increments the counter by one. + * + * @throws IllegalStateException if the counter is already maxed + */ + public void increment() { + incrementByte(counter.length - 1); } - if (counter[index] == (byte) 0xff) { - incrementByte(index - 1); - counter[index] = (byte) 0x00; - } else { - counter[index]++; + private void incrementByte(int index) { + if (isMaxed()) { + throw new IllegalStateException("Counter is already maxed"); + } + + if (counter[index] == (byte) 0xff) { + incrementByte(index - 1); + counter[index] = (byte) 0x00; + } else { + counter[index]++; + } } - } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java index b23966170..a01331275 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/data/CryptoDataFactoryImpl.java @@ -19,58 +19,89 @@ */ package org.eclipse.tractusx.edc.data.encryption.data; -import lombok.Value; import org.bouncycastle.util.encoders.Base64; public class CryptoDataFactoryImpl implements CryptoDataFactory { - public DecryptedData decryptedFromText(String text) { - final byte[] bytes = text.getBytes(); - final String base64 = Base64.toBase64String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public DecryptedData decryptedFromBase64(String base64) { - final byte[] bytes = Base64.decode(base64); - final String text = new String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public DecryptedData decryptedFromBytes(byte[] bytes) { - final String base64 = Base64.toBase64String(bytes); - final String text = new String(bytes); - return new DecryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromText(String text) { - final byte[] bytes = text.getBytes(); - final String base64 = Base64.toBase64String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromBase64(String base64) { - final byte[] bytes = Base64.decode(base64); - final String text = new String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - public EncryptedData encryptedFromBytes(byte[] bytes) { - final String base64 = Base64.toBase64String(bytes); - final String text = new String(bytes); - return new EncryptedDataImpl(bytes, base64, text); - } - - @Value - private static class DecryptedDataImpl implements DecryptedData { - byte[] bytes; - String base64; - String text; - } - - @Value - private static class EncryptedDataImpl implements EncryptedData { - byte[] bytes; - String base64; - String text; - } + public DecryptedData decryptedFromText(String text) { + final byte[] bytes = text.getBytes(); + final String base64 = Base64.toBase64String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public DecryptedData decryptedFromBase64(String base64) { + final byte[] bytes = Base64.decode(base64); + final String text = new String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public DecryptedData decryptedFromBytes(byte[] bytes) { + final String base64 = Base64.toBase64String(bytes); + final String text = new String(bytes); + return new DecryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromText(String text) { + final byte[] bytes = text.getBytes(); + final String base64 = Base64.toBase64String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromBase64(String base64) { + final byte[] bytes = Base64.decode(base64); + final String text = new String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + public EncryptedData encryptedFromBytes(byte[] bytes) { + final String base64 = Base64.toBase64String(bytes); + final String text = new String(bytes); + return new EncryptedDataImpl(bytes, base64, text); + } + + + private static class DecryptedDataImpl implements DecryptedData { + private final byte[] bytes; + private final String base64; + private final String text; + + private DecryptedDataImpl(byte[] bytes, String base64, String text) { + this.bytes = bytes; + this.base64 = base64; + this.text = text; + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public String getBase64() { + return base64; + } + } + + + private static class EncryptedDataImpl implements EncryptedData { + private final byte[] bytes; + private final String base64; + private final String text; + + private EncryptedDataImpl(byte[] bytes, String base64, String text) { + this.bytes = bytes; + this.base64 = base64; + this.text = text; + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public String getBase64() { + return base64; + } + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java index 725828acc..0723306e4 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterConfiguration.java @@ -21,12 +21,28 @@ package org.eclipse.tractusx.edc.data.encryption.encrypter; import java.time.Duration; -import lombok.NonNull; -import lombok.Value; -@Value + public class AesDataEncrypterConfiguration { - @NonNull String keySetAlias; - boolean cachingEnabled; - @NonNull Duration cachingDuration; + private final String keySetAlias; + private final boolean cachingEnabled; + private final Duration cachingDuration; + + public AesDataEncrypterConfiguration(String keySetAlias, boolean cachingEnabled, Duration cachingDuration) { + this.keySetAlias = keySetAlias; + this.cachingEnabled = cachingEnabled; + this.cachingDuration = cachingDuration; + } + + public Duration getCachingDuration() { + return cachingDuration; + } + + public boolean isCachingEnabled() { + return cachingEnabled; + } + + public String getKeySetAlias() { + return keySetAlias; + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java index 160f57df0..d8b4add87 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/AesDataEncrypterImpl.java @@ -20,15 +20,6 @@ package org.eclipse.tractusx.edc.data.encryption.encrypter; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Optional; -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; @@ -40,69 +31,85 @@ import org.eclipse.tractusx.edc.data.encryption.key.AesKey; import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; -@RequiredArgsConstructor +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Optional; +import javax.crypto.AEADBadTagException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + public class AesDataEncrypterImpl implements DataEncrypter { - private final CryptoAlgorithm encryptionStrategy; - private final Monitor monitor; - private final KeyProvider keyProvider; - private final CryptoAlgorithm algorithm; - private final CryptoDataFactory cryptoDataFactory; + private final CryptoAlgorithm encryptionStrategy; + private final Monitor monitor; + private final KeyProvider keyProvider; + private final CryptoAlgorithm algorithm; + private final CryptoDataFactory cryptoDataFactory; - @Override - public String encrypt(String value) { - DecryptedData decryptedData = cryptoDataFactory.decryptedFromText(value); - AesKey key = keyProvider.getEncryptionKey(); + public AesDataEncrypterImpl(CryptoAlgorithm encryptionStrategy, Monitor monitor, KeyProvider keyProvider, CryptoAlgorithm algorithm, CryptoDataFactory cryptoDataFactory) { + this.encryptionStrategy = encryptionStrategy; + this.monitor = monitor; + this.keyProvider = keyProvider; + this.algorithm = algorithm; + this.cryptoDataFactory = cryptoDataFactory; + } - try { - EncryptedData encryptedData = algorithm.encrypt(decryptedData, key); - return encryptedData.getBase64(); - } catch (IllegalBlockSizeException - | BadPaddingException - | InvalidKeyException - | InvalidAlgorithmParameterException - | NoSuchPaddingException - | NoSuchAlgorithmException e) { - throw new EdcException(e); + @Override + public String encrypt(String value) { + DecryptedData decryptedData = cryptoDataFactory.decryptedFromText(value); + AesKey key = keyProvider.getEncryptionKey(); + + try { + EncryptedData encryptedData = algorithm.encrypt(decryptedData, key); + return encryptedData.getBase64(); + } catch (IllegalBlockSizeException + | BadPaddingException + | InvalidKeyException + | InvalidAlgorithmParameterException + | NoSuchPaddingException + | NoSuchAlgorithmException e) { + throw new EdcException(e); + } } - } - @Override - public String decrypt(String value) { - EncryptedData encryptedData = cryptoDataFactory.encryptedFromBase64(value); + @Override + public String decrypt(String value) { + EncryptedData encryptedData = cryptoDataFactory.encryptedFromBase64(value); - return keyProvider - .getDecryptionKeySet() - .map(key -> decrypt(encryptedData, key)) - .filter(Optional::isPresent) - .map(Optional::get) - .map(DecryptedData::getBytes) - .map(String::new) - .findFirst() - .orElseThrow( - () -> - new EdcException( - DataEncryptionExtension.EXTENSION_NAME - + ": Failed to decrypt data. This can happen if the key set is empty, contains invalid keys, the decryption key rotated out of the key set or because the data was encrypted by a different algorithm.")); - } + return keyProvider + .getDecryptionKeySet() + .map(key -> decrypt(encryptedData, key)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(DecryptedData::getBytes) + .map(String::new) + .findFirst() + .orElseThrow( + () -> + new EdcException( + DataEncryptionExtension.EXTENSION_NAME + + ": Failed to decrypt data. This can happen if the key set is empty, contains invalid keys, the decryption key rotated out of the key set or because the data was encrypted by a different algorithm.")); + } - private Optional decrypt(EncryptedData data, AesKey key) { - try { - return Optional.of(encryptionStrategy.decrypt(data, key)); - } catch (AEADBadTagException e) { // thrown when wrong key is used for decryption - return Optional.empty(); - } catch (IllegalBlockSizeException - | BadPaddingException - | InvalidKeyException - | NoSuchPaddingException - | NoSuchAlgorithmException - | InvalidAlgorithmParameterException e) { - monitor.warning( - String.format( - DataEncryptionExtension.EXTENSION_NAME - + ": Exception decrypting data using key from rotating key set. %s", - e.getMessage())); - throw new EdcException(e); + private Optional decrypt(EncryptedData data, AesKey key) { + try { + return Optional.of(encryptionStrategy.decrypt(data, key)); + } catch (AEADBadTagException e) { // thrown when wrong key is used for decryption + return Optional.empty(); + } catch (IllegalBlockSizeException + | BadPaddingException + | InvalidKeyException + | NoSuchPaddingException + | NoSuchAlgorithmException + | InvalidAlgorithmParameterException e) { + monitor.warning( + String.format( + DataEncryptionExtension.EXTENSION_NAME + + ": Exception decrypting data using key from rotating key set. %s", + e.getMessage())); + throw new EdcException(e); + } } - } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java index 916ab245f..c40e20b08 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterFactory.java @@ -21,9 +21,6 @@ package org.eclipse.tractusx.edc.data.encryption.encrypter; -import static java.lang.String.format; - -import lombok.RequiredArgsConstructor; import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.security.Vault; @@ -36,45 +33,52 @@ import org.eclipse.tractusx.edc.data.encryption.provider.CachingKeyProvider; import org.eclipse.tractusx.edc.data.encryption.provider.KeyProvider; -@RequiredArgsConstructor -public class DataEncrypterFactory { +import static java.lang.String.format; - public static final String AES_ALGORITHM = "AES"; - public static final String NONE = "NONE"; +public class DataEncrypterFactory { - private final Vault vault; - private final Monitor monitor; - private final CryptoKeyFactory keyFactory; + public static final String AES_ALGORITHM = "AES"; + public static final String NONE = "NONE"; - public DataEncrypter createNoneEncrypter() { - return new DataEncrypter() { - @Override - public String encrypt(String data) { - return data; - } + private final Vault vault; + private final Monitor monitor; + private final CryptoKeyFactory keyFactory; - @Override - public String decrypt(String data) { - return data; - } - }; - } + public DataEncrypterFactory(Vault vault, Monitor monitor, CryptoKeyFactory keyFactory) { + this.vault = vault; + this.monitor = monitor; + this.keyFactory = keyFactory; + } - public DataEncrypter createAesEncrypter(AesDataEncrypterConfiguration configuration) { - KeyProvider keyProvider = - new AesKeyProvider(vault, configuration.getKeySetAlias(), keyFactory); + public DataEncrypter createNoneEncrypter() { + return new DataEncrypter() { + @Override + public String encrypt(String data) { + return data; + } - if (configuration.isCachingEnabled()) { - keyProvider = new CachingKeyProvider<>(keyProvider, configuration.getCachingDuration()); + @Override + public String decrypt(String data) { + return data; + } + }; } + + public DataEncrypter createAesEncrypter(AesDataEncrypterConfiguration configuration) { + KeyProvider keyProvider = + new AesKeyProvider(vault, configuration.getKeySetAlias(), keyFactory); - final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); - final AesAlgorithm algorithm = new AesAlgorithm(cryptoDataFactory); + if (configuration.isCachingEnabled()) { + keyProvider = new CachingKeyProvider<>(keyProvider, configuration.getCachingDuration()); + } - monitor.debug( - format( - "AES algorithm was initialised with SecureRandom algorithm '%s'", - algorithm.getAlgorithm())); - return new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); - } + final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); + final AesAlgorithm algorithm = new AesAlgorithm(cryptoDataFactory); + + monitor.debug( + format( + "AES algorithm was initialised with SecureRandom algorithm '%s'", + algorithm.getAlgorithm())); + return new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java index f3fa102a4..7a5b0fc15 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/key/CryptoKeyFactoryImpl.java @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.key; -import lombok.Value; import org.bouncycastle.util.encoders.Base64; public class CryptoKeyFactoryImpl implements CryptoKeyFactory { @@ -37,9 +36,24 @@ public AesKey fromBytes(byte[] key) { return new AesKeyImpl(key, Base64.toBase64String(key)); } - @Value + private static class AesKeyImpl implements AesKey { - byte[] bytes; - String base64; + private final byte[] bytes; + private final String base64; + + private AesKeyImpl(byte[] bytes, String base64) { + this.bytes = bytes; + this.base64 = base64; + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public String getBase64() { + return base64; + } } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java index 82b8eccdd..e740a6f43 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/AesKeyProvider.java @@ -19,51 +19,56 @@ */ package org.eclipse.tractusx.edc.data.encryption.provider; -import java.util.Arrays; -import java.util.function.Predicate; -import java.util.stream.Stream; -import lombok.RequiredArgsConstructor; import org.bouncycastle.util.encoders.Base64; import org.eclipse.edc.spi.security.Vault; import org.eclipse.tractusx.edc.data.encryption.DataEncryptionExtension; import org.eclipse.tractusx.edc.data.encryption.key.AesKey; import org.eclipse.tractusx.edc.data.encryption.key.CryptoKeyFactory; -@RequiredArgsConstructor +import java.util.Arrays; +import java.util.function.Predicate; +import java.util.stream.Stream; + public class AesKeyProvider implements KeyProvider { - private static final String KEY_SEPARATOR = ","; + private static final String KEY_SEPARATOR = ","; + + private final Vault vault; + private final String vaultKeyAlias; + private final CryptoKeyFactory cryptoKeyFactory; - private final Vault vault; - private final String vaultKeyAlias; - private final CryptoKeyFactory cryptoKeyFactory; + public AesKeyProvider(Vault vault, String vaultKeyAlias, CryptoKeyFactory cryptoKeyFactory) { + this.vault = vault; + this.vaultKeyAlias = vaultKeyAlias; + this.cryptoKeyFactory = cryptoKeyFactory; + } - @Override - public Stream getDecryptionKeySet() { - return getKeysStream(); - } + @Override + public AesKey getEncryptionKey() { + return getKeysStream() + .findFirst() + .orElseThrow( + () -> + new RuntimeException( + DataEncryptionExtension.EXTENSION_NAME + + ": Vault must contain at least one key.")); + } - @Override - public AesKey getEncryptionKey() { - return getKeysStream() - .findFirst() - .orElseThrow( - () -> - new RuntimeException( - DataEncryptionExtension.EXTENSION_NAME - + ": Vault must contain at least one key.")); - } + @Override + public Stream getDecryptionKeySet() { + return getKeysStream(); + } - private Stream getKeysStream() { - return Arrays.stream(getKeys().split(KEY_SEPARATOR)) - .map(String::trim) - .filter(Predicate.not(String::isEmpty)) - .map(Base64::decode) - .map(cryptoKeyFactory::fromBytes); - } + private Stream getKeysStream() { + return Arrays.stream(getKeys().split(KEY_SEPARATOR)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .map(Base64::decode) + .map(cryptoKeyFactory::fromBytes); + } - private String getKeys() { - String keys = vault.resolveSecret(vaultKeyAlias); - return keys == null ? "" : keys; - } + private String getKeys() { + String keys = vault.resolveSecret(vaultKeyAlias); + return keys == null ? "" : keys; + } } diff --git a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java index b4b490918..4819b6386 100644 --- a/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java +++ b/edc-extensions/data-encryption/src/main/java/org/eclipse/tractusx/edc/data/encryption/provider/CachingKeyProvider.java @@ -20,60 +20,73 @@ package org.eclipse.tractusx.edc.data.encryption.provider; +import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; + import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.NonNull; -import lombok.Value; -import org.eclipse.tractusx.edc.data.encryption.key.CryptoKey; public class CachingKeyProvider implements KeyProvider { - @NonNull private final KeyProvider decoratedProvider; - @NonNull private final Clock clock; - @NonNull private final Duration cacheExpiration; - - private CachedKeys cachedKeys; - - public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration) { - this(keyProvider, cacheExpiration, Clock.systemUTC()); - } - - public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration, Clock clock) { - - this.decoratedProvider = keyProvider; - this.cacheExpiration = cacheExpiration; - this.clock = clock; - } - - @Override - public T getEncryptionKey() { - checkCache(); - return cachedKeys.getEncryptionKey(); - } - - @Override - public Stream getDecryptionKeySet() { - checkCache(); - return cachedKeys.getDecryptionKeys().stream(); - } - - private void checkCache() { - if (cachedKeys == null || cachedKeys.expiration.isBefore(clock.instant())) { - T encryptionKey = decoratedProvider.getEncryptionKey(); - List decryptionKeys = decoratedProvider.getDecryptionKeySet().collect(Collectors.toList()); - cachedKeys = - new CachedKeys<>(encryptionKey, decryptionKeys, clock.instant().plus(cacheExpiration)); + private final KeyProvider decoratedProvider; + private final Clock clock; + private final Duration cacheExpiration; + + private CachedKeys cachedKeys; + + public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration) { + this(keyProvider, cacheExpiration, Clock.systemUTC()); + } + + public CachingKeyProvider(KeyProvider keyProvider, Duration cacheExpiration, Clock clock) { + this.decoratedProvider = Objects.requireNonNull(keyProvider); + this.cacheExpiration = Objects.requireNonNull(cacheExpiration); + this.clock = Objects.requireNonNull(clock); + } + + @Override + public T getEncryptionKey() { + checkCache(); + return cachedKeys.getEncryptionKey(); + } + + @Override + public Stream getDecryptionKeySet() { + checkCache(); + return cachedKeys.getDecryptionKeys().stream(); + } + + private void checkCache() { + if (cachedKeys == null || cachedKeys.expiration.isBefore(clock.instant())) { + T encryptionKey = decoratedProvider.getEncryptionKey(); + List decryptionKeys = decoratedProvider.getDecryptionKeySet().collect(Collectors.toList()); + cachedKeys = + new CachedKeys<>(encryptionKey, decryptionKeys, clock.instant().plus(cacheExpiration)); + } + } + + + private static class CachedKeys { + private final T encryptionKey; + private final List decryptionKeys; + private final Instant expiration; + + private CachedKeys(T encryptionKey, List decryptionKeys, Instant expiration) { + this.encryptionKey = encryptionKey; + this.decryptionKeys = decryptionKeys; + this.expiration = Objects.requireNonNull(expiration); + } + + public List getDecryptionKeys() { + return decryptionKeys; + } + + public T getEncryptionKey() { + return encryptionKey; + } } - } - - @Value - private static class CachedKeys { - T encryptionKey; - List decryptionKeys; - @NonNull Instant expiration; - } } diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java index 4d19927fb..d141887cf 100644 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesAlgorithmTest.java @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; -import lombok.SneakyThrows; import org.bouncycastle.util.encoders.Base64; import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactory; import org.eclipse.tractusx.edc.data.encryption.data.CryptoDataFactoryImpl; @@ -31,62 +30,69 @@ class AesAlgorithmTest { - private static final byte[] KEY_128_BIT = Base64.decode("dVUjmYJzbwVcntkFZU+lNQ=="); - private static final byte[] KEY_196_BIT = Base64.decode("NcgHzzRTUC+z396tWG9hqIbeihujz0m8"); - private static final byte[] KEY_256_BIT = - Base64.decode("OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="); + private static final byte[] KEY_128_BIT = Base64.decode("dVUjmYJzbwVcntkFZU+lNQ=="); + private static final byte[] KEY_196_BIT = Base64.decode("NcgHzzRTUC+z396tWG9hqIbeihujz0m8"); + private static final byte[] KEY_256_BIT = + Base64.decode("OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="); - private final AesAlgorithm strategy = new AesAlgorithm(new CryptoDataFactoryImpl()); - private final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); + private final AesAlgorithm strategy = new AesAlgorithm(new CryptoDataFactoryImpl()); + private final CryptoDataFactory cryptoDataFactory = new CryptoDataFactoryImpl(); - @Test - void test128BitKey() { - testKey(KEY_128_BIT); - } + @Test + void test128BitKey() { + testKey(KEY_128_BIT); + } - @Test - void test196BitKey() { - testKey(KEY_196_BIT); - } + @Test + void test196BitKey() { + testKey(KEY_196_BIT); + } - @Test - void test256BitKey() { - testKey(KEY_256_BIT); - } + @Test + void test256BitKey() { + testKey(KEY_256_BIT); + } - @Test - @SneakyThrows - void testSameDataEncryptedDifferently() { - final AesKey aesKey = createKey(KEY_128_BIT); - final DecryptedData expected = cryptoDataFactory.decryptedFromText("same data"); - final EncryptedData result1 = strategy.encrypt(expected, aesKey); - final EncryptedData result2 = strategy.encrypt(expected, aesKey); + @Test + void testSameDataEncryptedDifferently() { + final AesKey aesKey = createKey(KEY_128_BIT); + final DecryptedData expected = cryptoDataFactory.decryptedFromText("same data"); - Assertions.assertNotEquals(result1.getBase64(), result2.getBase64()); - } + try { + final EncryptedData result1 = strategy.encrypt(expected, aesKey); + final EncryptedData result2 = strategy.encrypt(expected, aesKey); - @SneakyThrows - void testKey(byte[] key) { - final AesKey aesKey = createKey(key); - final DecryptedData expected = cryptoDataFactory.decryptedFromText("I will be encrypted"); - final EncryptedData encryptedResult = strategy.encrypt(expected, aesKey); - final DecryptedData result = strategy.decrypt(encryptedResult, aesKey); + Assertions.assertNotEquals(result1.getBase64(), result2.getBase64()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } - Assertions.assertEquals(expected.getBase64(), result.getBase64()); - } - AesKey createKey(byte[] key) { - return new AesKey() { + void testKey(byte[] key) { + final AesKey aesKey = createKey(key); + final DecryptedData expected = cryptoDataFactory.decryptedFromText("I will be encrypted"); + try { + final EncryptedData encryptedResult = strategy.encrypt(expected, aesKey); + final DecryptedData result = strategy.decrypt(encryptedResult, aesKey); + Assertions.assertEquals(expected.getBase64(), result.getBase64()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } - @Override - public byte[] getBytes() { - return key; - } + AesKey createKey(byte[] key) { + return new AesKey() { - @Override - public String getBase64() { - return Base64.toBase64String(key); - } - }; - } + @Override + public byte[] getBytes() { + return key; + } + + @Override + public String getBase64() { + return Base64.toBase64String(key); + } + }; + } } diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java index ceebf50d6..f70a3bf70 100644 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/algorithms/aes/AesInitializationVectorIteratorTest.java @@ -20,61 +20,57 @@ */ package org.eclipse.tractusx.edc.data.encryption.algorithms.aes; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; -import lombok.SneakyThrows; import org.eclipse.tractusx.edc.data.encryption.util.ArrayUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; + class AesInitializationVectorIteratorTest { - @Test - @SneakyThrows - void testDistinctVectors() { - final int vectorCount = 100; - final SecureRandom secureRandom = new SecureRandom(); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(secureRandom); + @Test + void testDistinctVectors() { + final int vectorCount = 100; + final SecureRandom secureRandom = new SecureRandom(); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(secureRandom); - List vectors = new ArrayList<>(); - for (var i = 0; i < vectorCount; i++) { - vectors.add(iterator.next()); - } + List vectors = new ArrayList<>(); + for (var i = 0; i < vectorCount; i++) { + vectors.add(iterator.next()); + } - long distinctVectors = vectors.stream().map(ArrayUtil::byteArrayToHex).distinct().count(); - Assertions.assertEquals(vectorCount, distinctVectors); - } + long distinctVectors = vectors.stream().map(ArrayUtil::byteArrayToHex).distinct().count(); + Assertions.assertEquals(vectorCount, distinctVectors); + } - @Test - @SneakyThrows - void testHasNextTrueOnCounterContinuing() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + @Test + void testHasNextTrueOnCounterContinuing() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - Mockito.when(counter.isMaxed()).thenReturn(false); - Assertions.assertTrue(iterator.hasNext()); - } + Mockito.when(counter.isMaxed()).thenReturn(false); + Assertions.assertTrue(iterator.hasNext()); + } - @Test - @SneakyThrows - void testHasNextFalseOnCounterEnd() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + @Test + void testHasNextFalseOnCounterEnd() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - Mockito.when(counter.isMaxed()).thenReturn(true); - Assertions.assertFalse(iterator.hasNext()); - } + Mockito.when(counter.isMaxed()).thenReturn(true); + Assertions.assertFalse(iterator.hasNext()); + } - @Test - @SneakyThrows - void testNoSuchElementExceptionOnCounterEnd() { - ByteCounter counter = Mockito.mock(ByteCounter.class); - AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); + @Test + void testNoSuchElementExceptionOnCounterEnd() { + ByteCounter counter = Mockito.mock(ByteCounter.class); + AesInitializationVectorIterator iterator = new AesInitializationVectorIterator(counter); - Mockito.when(counter.isMaxed()).thenReturn(true); - Assertions.assertThrows(NoSuchElementException.class, iterator::next); - } + Mockito.when(counter.isMaxed()).thenReturn(true); + Assertions.assertThrows(NoSuchElementException.class, iterator::next); + } } diff --git a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java index aa9140629..6dcd103cb 100644 --- a/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java +++ b/edc-extensions/data-encryption/src/test/java/org/eclipse/tractusx/edc/data/encryption/encrypter/DataEncrypterAesComponentTest.java @@ -19,7 +19,6 @@ */ package org.eclipse.tractusx.edc.data.encryption.encrypter; -import lombok.SneakyThrows; import org.eclipse.edc.connector.transfer.dataplane.spi.security.DataEncrypter; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.security.Vault; @@ -42,64 +41,68 @@ @SuppressWarnings("FieldCanBeLocal") class DataEncrypterAesComponentTest { - private static final String KEY_128_BIT_BASE_64 = "7h6sh6t6tchCmNnHjK2kFA=="; - private static final String KEY_256_BIT_BASE_64 = "OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="; - - private DataEncrypter dataEncrypter; - private CryptoAlgorithm algorithm; - private KeyProvider keyProvider; - private CryptoKeyFactory cryptoKeyFactory; - private CryptoDataFactory cryptoDataFactory; - - // mocks - private Monitor monitor; - private Vault vault; - - @BeforeEach - void setup() { - monitor = Mockito.mock(Monitor.class); - vault = Mockito.mock(Vault.class); - - cryptoKeyFactory = new CryptoKeyFactoryImpl(); - cryptoDataFactory = new CryptoDataFactoryImpl(); - algorithm = new AesAlgorithm(cryptoDataFactory); - keyProvider = new AesKeyProvider(vault, "foo", cryptoKeyFactory); - - dataEncrypter = - new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); - } - - @Test - @SneakyThrows - void testKeyRotation() { - Mockito.when(vault.resolveSecret(Mockito.anyString())) - .thenReturn( - String.format( - "%s, %s, %s, %s", - KEY_128_BIT_BASE_64, - KEY_128_BIT_BASE_64, - KEY_128_BIT_BASE_64, - KEY_256_BIT_BASE_64)); - - final AesKey key256Bit = cryptoKeyFactory.fromBase64(KEY_256_BIT_BASE_64); - final String expectedResult = "hello"; - final DecryptedData decryptedResult = cryptoDataFactory.decryptedFromText(expectedResult); - final EncryptedData encryptedResult = algorithm.encrypt(decryptedResult, key256Bit); - - var result = dataEncrypter.decrypt(encryptedResult.getBase64()); - - Assertions.assertEquals(expectedResult, result); - } - - @Test - void testEncryption() { - Mockito.when(vault.resolveSecret(Mockito.anyString())).thenReturn(KEY_128_BIT_BASE_64); - - final String expectedResult = "hello world!"; - - var encryptedResult = dataEncrypter.encrypt(expectedResult); - var result = dataEncrypter.decrypt(encryptedResult); - - Assertions.assertEquals(expectedResult, result); - } + private static final String KEY_128_BIT_BASE_64 = "7h6sh6t6tchCmNnHjK2kFA=="; + private static final String KEY_256_BIT_BASE_64 = "OSD+3NcZAmS/6UXbq6NL8UL+aQIAJDLL7BE2rBX5MtA="; + + private DataEncrypter dataEncrypter; + private CryptoAlgorithm algorithm; + private KeyProvider keyProvider; + private CryptoKeyFactory cryptoKeyFactory; + private CryptoDataFactory cryptoDataFactory; + + // mocks + private Monitor monitor; + private Vault vault; + + @BeforeEach + void setup() { + monitor = Mockito.mock(Monitor.class); + vault = Mockito.mock(Vault.class); + + cryptoKeyFactory = new CryptoKeyFactoryImpl(); + cryptoDataFactory = new CryptoDataFactoryImpl(); + algorithm = new AesAlgorithm(cryptoDataFactory); + keyProvider = new AesKeyProvider(vault, "foo", cryptoKeyFactory); + + dataEncrypter = + new AesDataEncrypterImpl(algorithm, monitor, keyProvider, algorithm, cryptoDataFactory); + } + + @Test + void testKeyRotation() { + Mockito.when(vault.resolveSecret(Mockito.anyString())) + .thenReturn( + String.format( + "%s, %s, %s, %s", + KEY_128_BIT_BASE_64, + KEY_128_BIT_BASE_64, + KEY_128_BIT_BASE_64, + KEY_256_BIT_BASE_64)); + + final AesKey key256Bit = cryptoKeyFactory.fromBase64(KEY_256_BIT_BASE_64); + final String expectedResult = "hello"; + final DecryptedData decryptedResult = cryptoDataFactory.decryptedFromText(expectedResult); + + try { + final EncryptedData encryptedResult = algorithm.encrypt(decryptedResult, key256Bit); + + var result = dataEncrypter.decrypt(encryptedResult.getBase64()); + + Assertions.assertEquals(expectedResult, result); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + void testEncryption() { + Mockito.when(vault.resolveSecret(Mockito.anyString())).thenReturn(KEY_128_BIT_BASE_64); + + final String expectedResult = "hello world!"; + + var encryptedResult = dataEncrypter.encrypt(expectedResult); + var result = dataEncrypter.decrypt(encryptedResult); + + Assertions.assertEquals(expectedResult, result); + } } diff --git a/edc-extensions/hashicorp-vault/build.gradle.kts b/edc-extensions/hashicorp-vault/build.gradle.kts index 90758edd0..1fc98b31b 100644 --- a/edc-extensions/hashicorp-vault/build.gradle.kts +++ b/edc-extensions/hashicorp-vault/build.gradle.kts @@ -9,7 +9,7 @@ dependencies { implementation(edc.junit) implementation(libs.bouncyCastle.bcpkix) implementation(libs.okhttp) - implementation("org.testcontainers:junit-jupiter:1.17.6") - implementation("org.testcontainers:vault:1.17.6") + implementation("org.testcontainers:vault:1.18.0") + implementation("org.testcontainers:junit-jupiter:1.18.0") testImplementation(libs.mockito.inline) } diff --git a/edc-extensions/postgresql-migration/build.gradle.kts b/edc-extensions/postgresql-migration/build.gradle.kts index 8d7b1fa05..cb04877c0 100644 --- a/edc-extensions/postgresql-migration/build.gradle.kts +++ b/edc-extensions/postgresql-migration/build.gradle.kts @@ -11,5 +11,5 @@ dependencies { implementation(edc.sql.assetindex) implementation(edc.sql.core) - implementation("org.flywaydb:flyway-core:9.15.2") + implementation("org.flywaydb:flyway-core:9.16.3") } diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index 1000005de..2628ce71e 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -16,12 +16,12 @@ dependencies { implementation(project(":edc-extensions:transferprocess-sftp-provisioner")) - testImplementation("com.google.code.gson:gson:2.10") + testImplementation("com.google.code.gson:gson:2.10.1") testImplementation("org.apache.httpcomponents:httpclient:4.5.14") testImplementation("org.junit.platform:junit-platform-suite:1.9.2") - testImplementation("io.cucumber:cucumber-java:7.11.1") - testImplementation("io.cucumber:cucumber-junit-platform-engine:7.11.1") - testImplementation("org.slf4j:slf4j-api:2.0.3") + testImplementation("io.cucumber:cucumber-java:7.11.2") + testImplementation("io.cucumber:cucumber-junit-platform-engine:7.11.2") + testImplementation("org.slf4j:slf4j-api:2.0.7") testImplementation(libs.restAssured) testImplementation(libs.postgres) testImplementation(libs.awaitility) @@ -38,4 +38,4 @@ tasks.withType(Test::class) { // do not publish edcBuild { publish.set(false) -} \ No newline at end of file +} diff --git a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml index d3248326b..7d69beb1d 100644 --- a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml +++ b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/Chart.yaml @@ -52,12 +52,6 @@ dependencies: repository: https://charts.bitnami.com/bitnami condition: install.postgresql - - name: backend-service - version: 0.0.6 - repository: https://denisneuling.github.io/cx-backend-service - alias: backend - condition: install.backendservice - # MinIo - name: minio alias: minio diff --git a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml index 57779134b..07c1e0b3b 100644 --- a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml +++ b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml @@ -8,19 +8,6 @@ install: postgresql: true vault: true minio: true - backendservice: true - -################### -# Backend Service # -################### -backend: - fullnameOverride: "backend" - service: - type: NodePort - frontend: - port: 8080 - backend: - port: 8081 ######## diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendDataService.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendDataService.java new file mode 100644 index 000000000..21beba150 --- /dev/null +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendDataService.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.tests; + + +import java.io.InputStream; +import java.util.List; + +public interface BackendDataService { + List list(String path); + + boolean exists(String path); + + byte[] get(String path); + + void post(String path, InputStream inputStream, long length); + + void post(String path, InputStream inputStream); + + void post(String path, byte[] content); + + void delete(String path); +} diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java deleted file mode 100644 index 6b2a5ee2e..000000000 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceBackendAPI.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2022 Mercedes-Benz Tech Innovation GmbH - * Copyright (c) 2021,2022 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * 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 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.eclipse.tractusx.edc.tests; - -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URI; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.client.HttpClient; -import org.apache.http.client.HttpResponseException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.entity.BasicHttpEntity; -import org.apache.http.entity.ContentType; -import org.apache.http.impl.client.AbstractResponseHandler; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.util.EntityUtils; - -@Slf4j -public class BackendServiceBackendAPI { - private static final String HTTP_HEADER_ACCEPT = "Accept"; - private static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type"; - private static final String PATH_ROOT = "/"; - private final String backendServiceBackendApiUrl; - private final HttpClient httpClient; - - public BackendServiceBackendAPI(@NonNull final String backendServiceBackendApiUrl) { - this.backendServiceBackendApiUrl = backendServiceBackendApiUrl; - this.httpClient = HttpClientBuilder.create().build(); - } - - /** Lists all files and directories associated by a backend-service path. */ - @SneakyThrows - public List list(/* @Nullable */ final String path) { - final URI uri = - new URIBuilder(backendServiceBackendApiUrl) - .setPath(Optional.ofNullable(path).orElse(PATH_ROOT)) - .build(); - final HttpGet get = new HttpGet(uri); - get.setHeader(HTTP_HEADER_ACCEPT, ContentType.APPLICATION_JSON.getMimeType()); - - log.debug(String.format("Send %-6s %s", get.getMethod(), get.getURI())); - - return httpClient.execute(get, ListResponseHandler.INSTANCE); - } - - /** Proves existence of a file or directory associated by a backend-service path. */ - @SneakyThrows - public boolean exists(@NonNull final String path) { - final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); - final HttpHead head = new HttpHead(uri); - - log.debug(String.format("Send %-6s %s", head.getMethod(), head.getURI())); - - return httpClient.execute(head, ExistsResponseHandler.INSTANCE); - } - - /** Retrieves file content associated by a backend-service path. */ - @SneakyThrows - public byte[] get(@NonNull final String path) { - final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); - final HttpGet get = new HttpGet(uri); - get.setHeader(HTTP_HEADER_ACCEPT, ContentType.APPLICATION_OCTET_STREAM.getMimeType()); - - log.debug(String.format("Send %-6s %s", get.getMethod(), get.getURI())); - - return httpClient.execute(get, GetResponseHandler.INSTANCE); - } - - /** - * Creates a file associated by a backend-service path. If existing truncates and recreates that - * file - */ - @SneakyThrows - public void post( - @NonNull final String path, @NonNull final InputStream inputStream, long length) { - final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); - final HttpPost post = new HttpPost(uri); - post.addHeader(HTTP_HEADER_CONTENT_TYPE, ContentType.APPLICATION_OCTET_STREAM.getMimeType()); - final BasicHttpEntity entity = new BasicHttpEntity(); - entity.setContent(inputStream); - entity.setContentLength(length); - - post.setEntity(entity); - - log.debug(String.format("Send %-6s %s", post.getMethod(), post.getURI())); - - httpClient.execute(post, PostResponseHandler.INSTANCE); - } - - @SneakyThrows - public void post(@NonNull final String path, @NonNull final InputStream inputStream) { - post(path, inputStream, -1); - } - - @SneakyThrows - public void post(@NonNull final String path, @NonNull final byte[] content) { - try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content)) { - post(path, byteArrayInputStream, content.length); - } - } - - /** Deletes files (and directories in a recursive manner) associated by a backend-service path. */ - @SneakyThrows - public void delete(@NonNull final String path) { - final URI uri = new URIBuilder(backendServiceBackendApiUrl).setPath(path).build(); - final HttpDelete delete = new HttpDelete(uri); - - httpClient.execute(delete, DeleteResponseHandler.INSTANCE); - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static final class PostResponseHandler implements ResponseHandler { - public static final DeleteResponseHandler INSTANCE = new DeleteResponseHandler(); - - private static final List ACCEPTABLE_STATUS_CODES = - Arrays.asList(HttpStatus.SC_OK, HttpStatus.SC_ACCEPTED, HttpStatus.SC_CREATED); - - @Override - public Void handleResponse(@NonNull final HttpResponse response) throws IOException { - final StatusLine statusLine = response.getStatusLine(); - final Integer code = statusLine.getStatusCode(); - final HttpEntity entity = response.getEntity(); - - // not interested into content so throw it away - EntityUtils.consume(entity); - - if (ACCEPTABLE_STATUS_CODES.contains(code)) { - return null; - } - - throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); - } - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static class DeleteResponseHandler implements ResponseHandler { - public static final DeleteResponseHandler INSTANCE = new DeleteResponseHandler(); - - private static final List ACCEPTABLE_STATUS_CODES = - Arrays.asList( - HttpStatus.SC_OK, - HttpStatus.SC_ACCEPTED, - HttpStatus.SC_NO_CONTENT, - HttpStatus.SC_NOT_FOUND); - - @Override - public Void handleResponse(@NonNull final HttpResponse response) throws IOException { - final StatusLine statusLine = response.getStatusLine(); - final Integer code = statusLine.getStatusCode(); - - // not interested into content so throw it away - Optional.ofNullable(response.getEntity()).ifPresent(EntityUtils::consumeQuietly); - - if (ACCEPTABLE_STATUS_CODES.contains(code)) { - return null; - } - - throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); - } - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static class GetResponseHandler extends AbstractResponseHandler { - public static final GetResponseHandler INSTANCE = new GetResponseHandler(); - - private static byte[] readAllBytes(@NonNull final InputStream stream) throws IOException { - final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - final byte[] data = new byte[16384]; - - int i; - while ((i = stream.read(data, 0, data.length)) != -1) { - byteArrayOutputStream.write(data, 0, i); - } - - return byteArrayOutputStream.toByteArray(); - } - - @Override - public byte[] handleEntity(@NonNull final HttpEntity entity) throws IOException { - try (final InputStream inputStream = entity.getContent()) { - return readAllBytes(inputStream); - } - } - } - - @NoArgsConstructor(access = AccessLevel.PRIVATE) - private static class ExistsResponseHandler implements ResponseHandler { - public static final ExistsResponseHandler INSTANCE = new ExistsResponseHandler(); - - @Override - public Boolean handleResponse(@NonNull final HttpResponse response) - throws HttpResponseException { - final StatusLine statusLine = response.getStatusLine(); - final int code = statusLine.getStatusCode(); - - Optional.ofNullable(response.getEntity()).ifPresent(EntityUtils::consumeQuietly); - - switch (code) { - case HttpStatus.SC_OK: - return true; - case HttpStatus.SC_NOT_FOUND: - return false; - default: - throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); - } - } - } - - private static class ListResponseHandler extends GsonResponseHandler> { - public static final ListResponseHandler INSTANCE = new ListResponseHandler(); - - private ListResponseHandler() { - super(new TypeToken<>() {}); // JVM type erasure: Keep generic args! - } - } - - @RequiredArgsConstructor(access = AccessLevel.PROTECTED) - private static class GsonResponseHandler extends AbstractResponseHandler { - private static final Gson GSON = new Gson(); - - @NonNull private final TypeToken typeToken; - - @Override - public T handleEntity(@NonNull final HttpEntity entity) throws IOException { - try (final InputStreamReader inputStreamReader = new InputStreamReader(entity.getContent())) { - return GSON.fromJson(inputStreamReader, typeToken.getType()); - } - } - } -} diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java index 05960ddf7..fa1d2467a 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/BackendServiceSteps.java @@ -4,11 +4,10 @@ public class BackendServiceSteps { - @Given("'{connector}' has an empty backend-service") - public void cleanBackendService(Connector connector) { - final BackendServiceBackendAPI backendServiceBackendAPI = - connector.getBackendServiceBackendAPI(); + @Given("'{connector}' has an empty backend-service") + public void cleanBackendService(Connector connector) { + var backendServiceBackendAPI = connector.getBackendServiceBackendAPI(); - backendServiceBackendAPI.list("/").forEach(backendServiceBackendAPI::delete); - } + backendServiceBackendAPI.list("/").forEach(backendServiceBackendAPI::delete); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java index 9ca1bcb19..d4e2ea7a8 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Connector.java @@ -20,48 +20,71 @@ package org.eclipse.tractusx.edc.tests; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; + import org.eclipse.tractusx.edc.tests.util.DatabaseCleaner; import org.eclipse.tractusx.edc.tests.util.S3Client; -@RequiredArgsConstructor +import static org.mockito.Mockito.mock; + public class Connector { - @NonNull @Getter private final String name; + private final String name; + + private final Environment environment; + + private final DataManagementAPI dataManagementAPI; + + private final DatabaseCleaner databaseCleaner; + + + private final S3Client s3Client; + + public Connector(String name, Environment environment) { + this.name = name; + this.environment = environment; + dataManagementAPI = loadDataManagementAPI(); + databaseCleaner = loadDatabaseCleaner(); + s3Client = createS3Client(); + } + + public BackendDataService getBackendServiceBackendAPI() { + return mock(BackendDataService.class); + } - @Getter @NonNull private final Environment environment; + public DatabaseCleaner getDatabaseCleaner() { + return databaseCleaner; + } - @Getter(lazy = true) - private final DataManagementAPI dataManagementAPI = loadDataManagementAPI(); + public DataManagementAPI getDataManagementAPI() { + return dataManagementAPI; + } - @Getter(lazy = true) - private final BackendServiceBackendAPI backendServiceBackendAPI = loadBackendServiceBackendAPI(); + public Environment getEnvironment() { + return environment; + } - @Getter(lazy = true) - private final DatabaseCleaner databaseCleaner = loadDatabaseCleaner(); + public S3Client getS3Client() { + return s3Client; + } - @Getter(lazy = true) - private final S3Client s3Client = createS3Client(); + public String getName() { + return name; + } - private DataManagementAPI loadDataManagementAPI() { - return new DataManagementAPI( - environment.getDataManagementUrl(), environment.getDataManagementAuthKey()); - } + private DataManagementAPI loadDataManagementAPI() { + return new DataManagementAPI( + environment.getDataManagementUrl(), environment.getDataManagementAuthKey()); + } - private DatabaseCleaner loadDatabaseCleaner() { - return new DatabaseCleaner( - environment.getDatabaseUrl(), - environment.getDatabaseUser(), - environment.getDatabasePassword()); - } + private DatabaseCleaner loadDatabaseCleaner() { + return new DatabaseCleaner( + environment.getDatabaseUrl(), + environment.getDatabaseUser(), + environment.getDatabasePassword()); + } - private BackendServiceBackendAPI loadBackendServiceBackendAPI() { - return new BackendServiceBackendAPI(environment.getBackendServiceBackendApiUrl()); - } - private S3Client createS3Client() { - return new S3Client(environment); - } + private S3Client createS3Client() { + return new S3Client(environment); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java index 7a8ef81a1..364e266f3 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ConnectorFactory.java @@ -20,24 +20,25 @@ package org.eclipse.tractusx.edc.tests; -import java.util.HashMap; import java.util.Locale; import java.util.Map; -import lombok.NonNull; -import lombok.experimental.UtilityClass; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + -@UtilityClass public class ConnectorFactory { - private static final Map CONNECTOR_CACHE = new HashMap<>(); + private static final Map CONNECTOR_CACHE = new ConcurrentHashMap<>(); - public static Connector byName(@NonNull final String name) { - return CONNECTOR_CACHE.computeIfAbsent( - name.toUpperCase(Locale.ROOT), k -> createConnector(name)); - } + public static Connector byName(String name) { + Objects.requireNonNull(name); + return CONNECTOR_CACHE.computeIfAbsent( + name.toUpperCase(Locale.ROOT), k -> createConnector(name)); + } - private static Connector createConnector(@NonNull final String name) { - final Environment environment = Environment.byName(name); + private static Connector createConnector(String name) { + Objects.requireNonNull(name); + Environment environment = Environment.byName(name); - return new Connector(name, environment); - } + return new Connector(name, environment); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java index 67b484e38..6a7de2ceb 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Constants.java @@ -20,19 +20,16 @@ package org.eclipse.tractusx.edc.tests; -import lombok.experimental.UtilityClass; - -@UtilityClass public final class Constants { - public static final String DATA_MANAGEMENT_URL = "DATA_MANAGEMENT_URL"; - public static final String DATA_MANAGEMENT_API_AUTH_KEY = "DATA_MANAGEMENT_API_AUTH_KEY"; - public static final String IDS_URL = "IDS_URL"; - public static final String DATA_PLANE_URL = "DATA_PLANE_URL"; - public static final String BACKEND_SERVICE_BACKEND_API_URL = "BACKEND_SERVICE_BACKEND_API_URL"; - public static final String DATABASE_URL = "DATABASE_URL"; - public static final String DATABASE_USER = "DATABASE_USER"; - public static final String DATABASE_PASSWORD = "DATABASE_PASSWORD"; - public static final String EDC_AWS_ENDPOINT_OVERRIDE = "EDC_AWS_ENDPOINT_OVERRIDE"; - public static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"; - public static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; + public static final String DATA_MANAGEMENT_URL = "DATA_MANAGEMENT_URL"; + public static final String DATA_MANAGEMENT_API_AUTH_KEY = "DATA_MANAGEMENT_API_AUTH_KEY"; + public static final String IDS_URL = "IDS_URL"; + public static final String DATA_PLANE_URL = "DATA_PLANE_URL"; + public static final String BACKEND_SERVICE_BACKEND_API_URL = "BACKEND_SERVICE_BACKEND_API_URL"; + public static final String DATABASE_URL = "DATABASE_URL"; + public static final String DATABASE_USER = "DATABASE_USER"; + public static final String DATABASE_PASSWORD = "DATABASE_PASSWORD"; + public static final String EDC_AWS_ENDPOINT_OVERRIDE = "EDC_AWS_ENDPOINT_OVERRIDE"; + public static final String AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"; + public static final String AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ControlPlaneAdapterSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ControlPlaneAdapterSteps.java index d29a13aa4..e786c789a 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ControlPlaneAdapterSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/ControlPlaneAdapterSteps.java @@ -22,64 +22,66 @@ import com.google.gson.Gson; import io.cucumber.datatable.DataTable; -import java.io.IOException; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.HttpClientBuilder; import org.eclipse.edc.spi.system.health.HealthStatus; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Map; -@Slf4j public class ControlPlaneAdapterSteps { - private EndpointDataReference endpointDataReference; + private static final Logger log = LoggerFactory.getLogger(ControlPlaneAdapterSteps.class); + private EndpointDataReference endpointDataReference; - /* - * TODO: see of EndToEndTransfer.feature - * the current Bussinnes test is not running, because of a possible rare condition in the CI pipeline - * regarding the contract validity: see https://github.com/eclipse-edc/Connector/issues/2514 - */ + /* + * TODO: see of EndToEndTransfer.feature + * the current Bussinnes test is not running, because of a possible rare condition in the CI pipeline + * regarding the contract validity: see https://github.com/eclipse-edc/Connector/issues/2514 + */ - // @When("'{connector}' gets a request endpoint from '{connector}'") - public void getEndPointFromGetRequest(Connector consumer, Connector receiver, DataTable table) - throws IOException { + // @When("'{connector}' gets a request endpoint from '{connector}'") + public void getEndPointFromGetRequest(Connector consumer, Connector receiver, DataTable table) + throws IOException { - final DataManagementAPI dataManagementAPI = consumer.getDataManagementAPI(); - final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + DataManagementAPI dataManagementAPI = consumer.getDataManagementAPI(); + String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - for (Map map : table.asMaps()) { - final String assetId = map.get("asset id"); + for (Map map : table.asMaps()) { + String assetId = map.get("asset id"); - endpointDataReference = dataManagementAPI.getEdcEndpoint(assetId, receiverIdsUrl); + endpointDataReference = dataManagementAPI.getEdcEndpoint(assetId, receiverIdsUrl); - log.debug("endpointDataReference in controlplane" + endpointDataReference.toString()); + log.debug("endpointDataReference in controlplane" + endpointDataReference.toString()); + } } - } - /* - * TODO: see EndToEndTransfer.feature - * the current Bussinnes test is not running, because of a possible rare condition in the CI pipeline - * regarding the contract validity: see https://github.com/eclipse-edc/Connector/issues/2514 - */ + /* + * TODO: see EndToEndTransfer.feature + * the current Bussinnes test is not running, because of a possible rare condition in the CI pipeline + * regarding the contract validity: see https://github.com/eclipse-edc/Connector/issues/2514 + */ - // @Then("'{connector}' asks for the asset from the endpoint") - public void receiveEndpoint(Connector consumer) throws IOException { + // @Then("'{connector}' asks for the asset from the endpoint") + public void receiveEndpoint(Connector consumer) throws IOException { - var requestUrl = endpointDataReference.getEndpoint(); - var key = endpointDataReference.getAuthKey(); - var value = endpointDataReference.getAuthCode(); - var httpClient = HttpClientBuilder.create().build(); - var get = new HttpGet(requestUrl); - get.addHeader(key, value); - final CloseableHttpResponse response = httpClient.execute(get); - var bytes = response.getEntity().getContent().readAllBytes(); - var result = new String(bytes); - var resultTransformed = new Gson().fromJson(result, HealthStatus.class); + var requestUrl = endpointDataReference.getEndpoint(); + var key = endpointDataReference.getAuthKey(); + var value = endpointDataReference.getAuthCode(); + var httpClient = HttpClientBuilder.create().build(); + var get = new HttpGet(requestUrl); + get.addHeader(key, value); + CloseableHttpResponse response = httpClient.execute(get); + var bytes = response.getEntity().getContent().readAllBytes(); + var result = new String(bytes); + var resultTransformed = new Gson().fromJson(result, HealthStatus.class); - Assertions.assertTrue(resultTransformed.isHealthy()); - Assertions.assertFalse(resultTransformed.getComponentResults().isEmpty()); - } + Assertions.assertTrue(resultTransformed.isHealthy()); + Assertions.assertFalse(resultTransformed.getComponentResults().isEmpty()); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java index 735cbf175..d67dc77ca 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/DataManagementAPI.java @@ -23,17 +23,6 @@ import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.Data; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; @@ -45,628 +34,706 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicHeader; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; -import org.eclipse.tractusx.edc.tests.data.*; +import org.eclipse.tractusx.edc.tests.data.Asset; +import org.eclipse.tractusx.edc.tests.data.BusinessPartnerNumberConstraint; +import org.eclipse.tractusx.edc.tests.data.Constraint; +import org.eclipse.tractusx.edc.tests.data.ContractDefinition; +import org.eclipse.tractusx.edc.tests.data.ContractNegotiation; +import org.eclipse.tractusx.edc.tests.data.ContractNegotiationState; +import org.eclipse.tractusx.edc.tests.data.ContractOffer; +import org.eclipse.tractusx.edc.tests.data.DataAddress; +import org.eclipse.tractusx.edc.tests.data.HttpProxySinkDataAddress; +import org.eclipse.tractusx.edc.tests.data.HttpProxySourceDataAddress; +import org.eclipse.tractusx.edc.tests.data.Negotiation; +import org.eclipse.tractusx.edc.tests.data.NullDataAddress; +import org.eclipse.tractusx.edc.tests.data.OrConstraint; +import org.eclipse.tractusx.edc.tests.data.PayMeConstraint; +import org.eclipse.tractusx.edc.tests.data.Permission; +import org.eclipse.tractusx.edc.tests.data.Policy; +import org.eclipse.tractusx.edc.tests.data.S3DataAddress; +import org.eclipse.tractusx.edc.tests.data.Transfer; +import org.eclipse.tractusx.edc.tests.data.TransferProcess; +import org.eclipse.tractusx.edc.tests.data.TransferProcessState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; -@Slf4j public class DataManagementAPI { - private static final String ASSET_PATH = "/assets"; - private static final String POLICY_PATH = "/policydefinitions"; - private static final String CONTRACT_DEFINITIONS_PATH = "/contractdefinitions"; - private static final String CATALOG_PATH = "/catalog"; - private static final String NEGOTIATIONS_PATH = "/contractnegotiations"; - private static final String TRANSFER_PATH = "/transferprocess"; - private static final String ADAPTER_PATH = "/adapter/asset/sync/"; - - private final String dataMgmtUrl; - private final String dataMgmtAuthKey; - private final CloseableHttpClient httpClient; - - public DataManagementAPI(String dataManagementUrl, String dataMgmtAuthKey) { - this.httpClient = HttpClientBuilder.create().build(); - this.dataMgmtUrl = dataManagementUrl; - this.dataMgmtAuthKey = dataMgmtAuthKey; - } - - public List requestCatalogFrom(String receivingConnectorUrl) throws IOException { - final String encodedUrl = URLEncoder.encode(receivingConnectorUrl, StandardCharsets.UTF_8); - final ManagementApiContractOfferCatalog catalog = - get( - CATALOG_PATH, - "providerUrl=" + encodedUrl, - new TypeToken() {}); - - log.debug("Received " + catalog.contractOffers.size() + " offers"); - - return catalog.contractOffers.stream().map(this::mapOffer).collect(Collectors.toList()); - } - - public Negotiation initiateNegotiation( - String receivingConnectorUrl, String definitionId, String assetId, Policy policy) - throws IOException { - final ManagementApiOffer offer = new ManagementApiOffer(); - offer.offerId = definitionId + ":foo"; - offer.assetId = assetId; - offer.policy = mapPolicy(policy); - offer.policy.permissions.forEach(p -> p.target = assetId); - - final ManagementApiNegotiationPayload negotiationPayload = - new ManagementApiNegotiationPayload(); - negotiationPayload.connectorAddress = receivingConnectorUrl; - negotiationPayload.offer = offer; - - final ManagementApiNegotiationResponse response = - post( - NEGOTIATIONS_PATH, - negotiationPayload, - new TypeToken() {}); - - if (response == null) - throw new RuntimeException( - "Initiated negotiation. Connector did not answer with negotiation ID."); - - log.info(String.format("Initiated negotiation (id=%s)", response.getId())); - - final String negotiationId = response.getId(); - return new Negotiation(negotiationId); - } - - public Transfer initiateTransferProcess( - String receivingConnectorUrl, - String contractAgreementId, - String assetId, - DataAddress dataAddress) - throws IOException { - final ManagementApiTransfer transfer = new ManagementApiTransfer(); - - transfer.connectorAddress = receivingConnectorUrl; - transfer.contractId = contractAgreementId; - transfer.assetId = assetId; - transfer.transferType = new ManagementApiTransferType(); - transfer.managedResources = false; - transfer.dataDestination = mapDataAddress(dataAddress); - transfer.protocol = "ids-multipart"; - - return initiateTransferProcess(transfer); - } - - public Transfer initiateTransferProcess( - String receivingConnectorUrl, - String contractAgreementId, - String assetId, - DataAddress dataAddress, - String receiverEndpoint) - throws IOException { - final ManagementApiTransfer transfer = new ManagementApiTransfer(); - - transfer.connectorAddress = receivingConnectorUrl; - transfer.contractId = contractAgreementId; - transfer.assetId = assetId; - transfer.transferType = new ManagementApiTransferType(); - transfer.managedResources = false; - transfer.dataDestination = mapDataAddress(dataAddress); - transfer.protocol = "ids-multipart"; - transfer.properties = new ManagementApiProperties(receiverEndpoint); - - return initiateTransferProcess(transfer); - } - - private Transfer initiateTransferProcess(ManagementApiTransfer transfer) throws IOException { - final ManagementApiTransferResponse response = - post(TRANSFER_PATH, transfer, new TypeToken() {}); - - if (response == null) - throw new RuntimeException( - "Initiated transfer process. Connector did not answer with transfer process ID."); - - log.info(String.format("Initiated transfer process (id=%s)", response.getId())); - - final String transferId = response.getId(); - return new Transfer(transferId); - } - - public Asset initiateTransferProcess( - String endpointUrl, String endpointAuthKey, String endpointAuthCode) throws IOException { - Header header = new BasicHeader(endpointAuthKey, endpointAuthCode); - return get(endpointUrl, header, new TypeToken() {}); - } - - public TransferProcess getTransferProcess(String id) throws IOException { - final ManagementApiTransferProcess transferProcess = - get(TRANSFER_PATH + "/" + id, new TypeToken() {}); - return mapTransferProcess(transferProcess); - } - - public ContractNegotiation getNegotiation(String id) throws IOException { - final ManagementApiNegotiation negotiation = - get(NEGOTIATIONS_PATH + "/" + id, new TypeToken() {}); - return mapNegotiation(negotiation); - } - - public List getNegotiations() throws IOException { - final List negotiations = - get(NEGOTIATIONS_PATH + "/", new TypeToken>() {}); - return negotiations.stream().map(this::mapNegotiation).collect(Collectors.toList()); - } + private static final Logger log = LoggerFactory.getLogger(DataManagementAPI.class); + private static final String ASSET_PATH = "/assets"; + private static final String POLICY_PATH = "/policydefinitions"; + private static final String CONTRACT_DEFINITIONS_PATH = "/contractdefinitions"; + private static final String CATALOG_PATH = "/catalog"; + private static final String NEGOTIATIONS_PATH = "/contractnegotiations"; + private static final String TRANSFER_PATH = "/transferprocess"; + private static final String ADAPTER_PATH = "/adapter/asset/sync/"; + + private final String dataMgmtUrl; + private final String dataMgmtAuthKey; + private final CloseableHttpClient httpClient; + + public DataManagementAPI(String dataManagementUrl, String dataMgmtAuthKey) { + httpClient = HttpClientBuilder.create().build(); + dataMgmtUrl = dataManagementUrl; + this.dataMgmtAuthKey = dataMgmtAuthKey; + } + + public List requestCatalogFrom(String receivingConnectorUrl) throws IOException { + String encodedUrl = URLEncoder.encode(receivingConnectorUrl, StandardCharsets.UTF_8); + ManagementApiContractOfferCatalog catalog = + get( + CATALOG_PATH, + "providerUrl=" + encodedUrl, + new TypeToken() { + }); + + log.debug("Received " + catalog.contractOffers.size() + " offers"); + + return catalog.contractOffers.stream().map(this::mapOffer).collect(Collectors.toList()); + } + + public Negotiation initiateNegotiation( + String receivingConnectorUrl, String definitionId, String assetId, Policy policy) + throws IOException { + ManagementApiOffer offer = new ManagementApiOffer(); + offer.offerId = definitionId + ":foo"; + offer.assetId = assetId; + offer.policy = mapPolicy(policy); + offer.policy.permissions.forEach(p -> p.target = assetId); + + ManagementApiNegotiationPayload negotiationPayload = + new ManagementApiNegotiationPayload(); + negotiationPayload.connectorAddress = receivingConnectorUrl; + negotiationPayload.offer = offer; + + ManagementApiNegotiationResponse response = + post( + NEGOTIATIONS_PATH, + negotiationPayload, + new TypeToken() { + }); + + if (response == null) { + throw new RuntimeException( + "Initiated negotiation. Connector did not answer with negotiation ID."); + } + + log.info(String.format("Initiated negotiation (id=%s)", response.getId())); + + String negotiationId = response.getId(); + return new Negotiation(negotiationId); + } + + public Transfer initiateTransferProcess( + String receivingConnectorUrl, + String contractAgreementId, + String assetId, + DataAddress dataAddress) + throws IOException { + ManagementApiTransfer transfer = new ManagementApiTransfer(); + + transfer.connectorAddress = receivingConnectorUrl; + transfer.contractId = contractAgreementId; + transfer.assetId = assetId; + transfer.transferType = new ManagementApiTransferType(); + transfer.managedResources = false; + transfer.dataDestination = mapDataAddress(dataAddress); + transfer.protocol = "ids-multipart"; + + return initiateTransferProcess(transfer); + } + + public Transfer initiateTransferProcess( + String receivingConnectorUrl, + String contractAgreementId, + String assetId, + DataAddress dataAddress, + String receiverEndpoint) + throws IOException { + ManagementApiTransfer transfer = new ManagementApiTransfer(); + + transfer.connectorAddress = receivingConnectorUrl; + transfer.contractId = contractAgreementId; + transfer.assetId = assetId; + transfer.transferType = new ManagementApiTransferType(); + transfer.managedResources = false; + transfer.dataDestination = mapDataAddress(dataAddress); + transfer.protocol = "ids-multipart"; + transfer.properties = new ManagementApiProperties(receiverEndpoint); + + return initiateTransferProcess(transfer); + } + + public Asset initiateTransferProcess( + String endpointUrl, String endpointAuthKey, String endpointAuthCode) throws IOException { + Header header = new BasicHeader(endpointAuthKey, endpointAuthCode); + return get(endpointUrl, header, new TypeToken() { + }); + } + + public TransferProcess getTransferProcess(String id) throws IOException { + ManagementApiTransferProcess transferProcess = + get(TRANSFER_PATH + "/" + id, new TypeToken() { + }); + return mapTransferProcess(transferProcess); + } + + public ContractNegotiation getNegotiation(String id) throws IOException { + ManagementApiNegotiation negotiation = + get(NEGOTIATIONS_PATH + "/" + id, new TypeToken() { + }); + return mapNegotiation(negotiation); + } + + public List getNegotiations() throws IOException { + List negotiations = + get(NEGOTIATIONS_PATH + "/", new TypeToken>() { + }); + return negotiations.stream().map(this::mapNegotiation).collect(Collectors.toList()); + } + + public void createAsset(Asset asset) throws IOException { + ManagementApiAssetCreate assetCreate = new ManagementApiAssetCreate(); + + assetCreate.asset = mapAsset(asset); + assetCreate.dataAddress = mapDataAddress(asset.getDataAddress()); + + post(ASSET_PATH, assetCreate); + } + + public void createPolicy(Policy policy) throws IOException { + post(POLICY_PATH, mapPolicyDefinition(policy)); + } + + public void createContractDefinition(ContractDefinition contractDefinition) throws IOException { + post(CONTRACT_DEFINITIONS_PATH, mapContractDefinition(contractDefinition)); + } + + public EndpointDataReference getEdcEndpoint(String assetId, String receivingConnectorUrl) + throws IOException { + String encodedUrl = ADAPTER_PATH + assetId + "?providerUrl=" + receivingConnectorUrl; + + EndpointDataReference endpoint = + get(encodedUrl, new TypeToken() { + }); + + return endpoint; + } + + private Transfer initiateTransferProcess(ManagementApiTransfer transfer) throws IOException { + ManagementApiTransferResponse response = + post(TRANSFER_PATH, transfer, new TypeToken() { + }); + + if (response == null) { + throw new RuntimeException( + "Initiated transfer process. Connector did not answer with transfer process ID."); + } - public void createAsset(Asset asset) throws IOException { - final ManagementApiAssetCreate assetCreate = new ManagementApiAssetCreate(); + log.info(String.format("Initiated transfer process (id=%s)", response.getId())); - assetCreate.asset = mapAsset(asset); - assetCreate.dataAddress = mapDataAddress(asset.getDataAddress()); + String transferId = response.getId(); + return new Transfer(transferId); + } + + private T get(String path, String params, TypeToken typeToken) throws IOException { + return get(path + "?" + params, typeToken); + } + + private T get(String path, TypeToken typeToken) throws IOException { + + HttpGet get = new HttpGet(dataMgmtUrl + path); + HttpResponse response = sendRequest(get); + byte[] json = response.getEntity().getContent().readAllBytes(); + + log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); + return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); + } + + private T get(String path, Header header, TypeToken typeToken) throws IOException { + + HttpGet get = new HttpGet(path); + get.addHeader(header); + HttpResponse response = sendRequest(get); + byte[] json = response.getEntity().getContent().readAllBytes(); + + log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); + return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); + } + + private void post(String path, Object object) throws IOException { + post(path, object, new TypeToken() { + }); + } + + private T post(String path, Object object, TypeToken typeToken) throws IOException { + String url = String.format("%s%s", dataMgmtUrl, path); + HttpPost post = new HttpPost(url); + post.addHeader("Content-Type", "application/json"); + + var json = new Gson().toJson(object); + + log.debug("POST Payload: " + json); + + post.setEntity(new StringEntity(json)); + CloseableHttpResponse response = sendRequest(post); + + T responseJson = null; + if (!typeToken.equals(new TypeToken() { + })) { + byte[] responseBytes = response.getEntity().getContent().readAllBytes(); + responseJson = + new Gson() + .fromJson(new String(responseBytes, StandardCharsets.UTF_8), typeToken.getType()); + } + + response.close(); + + return responseJson; + } + + private CloseableHttpResponse sendRequest(HttpRequestBase request) throws IOException { + request.addHeader("X-Api-Key", dataMgmtAuthKey); + + log.debug(String.format("Send %-6s %s", request.getMethod(), request.getURI())); + + CloseableHttpResponse response = httpClient.execute(request); + if (200 > response.getStatusLine().getStatusCode() + || response.getStatusLine().getStatusCode() >= 300) { + throw new RuntimeException( + String.format("Unexpected response: %s", response.getStatusLine())); + } + + return response; + } + + private ContractNegotiation mapNegotiation(ManagementApiNegotiation negotiation) { + + ContractNegotiationState state; + + switch (negotiation.state) { + case "ERROR": + state = ContractNegotiationState.ERROR; + break; + case "INITIAL": + state = ContractNegotiationState.INITIAL; + break; + case "DECLINED": + state = ContractNegotiationState.DECLINED; + break; + case "CONFIRMED": + state = ContractNegotiationState.CONFIRMED; + break; + default: + state = ContractNegotiationState.UNKNOWN; + } + + return new ContractNegotiation(negotiation.id, state, negotiation.contractAgreementId); + } + + private TransferProcess mapTransferProcess(ManagementApiTransferProcess transferProcess) { - post(ASSET_PATH, assetCreate); - } - - public void createPolicy(Policy policy) throws IOException { - post(POLICY_PATH, mapPolicyDefinition(policy)); - } - - public void createContractDefinition(ContractDefinition contractDefinition) throws IOException { - post(CONTRACT_DEFINITIONS_PATH, mapContractDefinition(contractDefinition)); - } - - public EndpointDataReference getEdcEndpoint(String assetId, String receivingConnectorUrl) - throws IOException { - final String encodedUrl = ADAPTER_PATH + assetId + "?providerUrl=" + receivingConnectorUrl; - - final EndpointDataReference endpoint = - get(encodedUrl, new TypeToken() {}); + TransferProcessState state; - return endpoint; - } + switch (transferProcess.state) { + case "COMPLETED": + state = TransferProcessState.COMPLETED; + break; + case "ERROR": + state = TransferProcessState.ERROR; + break; + default: + state = TransferProcessState.UNKNOWN; + } - private T get(String path, String params, TypeToken typeToken) throws IOException { - return get(path + "?" + params, typeToken); - } - - private T get(String path, TypeToken typeToken) throws IOException { - - final HttpGet get = new HttpGet(dataMgmtUrl + path); - final HttpResponse response = sendRequest(get); - final byte[] json = response.getEntity().getContent().readAllBytes(); + return new TransferProcess(transferProcess.id, state); + } + + private ManagementApiDataAddress mapDataAddress(DataAddress dataAddress) { + Objects.requireNonNull(dataAddress); + ManagementApiDataAddress apiObject = new ManagementApiDataAddress(); + + if (dataAddress instanceof HttpProxySourceDataAddress) { + var address = (HttpProxySourceDataAddress) dataAddress; + var properties = new HashMap(); + properties.put("type", "HttpData"); + properties.put("baseUrl", address.getBaseUrl()); + var oauth2Provision = address.getOauth2Provision(); + if (oauth2Provision != null) { + properties.put("oauth2:tokenUrl", oauth2Provision.getTokenUrl()); + properties.put("oauth2:clientId", oauth2Provision.getClientId()); + properties.put("oauth2:clientSecret", oauth2Provision.getClientSecret()); + properties.put("oauth2:scope", oauth2Provision.getScope()); + } + apiObject.setProperties(properties); + } else if (dataAddress instanceof HttpProxySinkDataAddress) { + apiObject.setProperties(Map.of("type", "HttpProxy")); + } else if (dataAddress instanceof S3DataAddress) { + S3DataAddress a = (S3DataAddress) dataAddress; + apiObject.setProperties( + Map.of( + "type", + "AmazonS3", + "bucketName", + a.getBucketName(), + "region", + a.getRegion(), + "keyName", + a.getKeyName())); + } else if (dataAddress instanceof NullDataAddress) { + // set something that passes validation + apiObject.setProperties(Map.of("type", "HttpData", "baseUrl", "http://localhost")); + } else { + throw new UnsupportedOperationException( + String.format( + "Cannot map data address of type %s to EDC domain", dataAddress.getClass())); + } + + return apiObject; + } + + private ManagementApiAsset mapAsset(Asset asset) { + Map properties = + Map.of( + ManagementApiAsset.ID, asset.getId(), + ManagementApiAsset.DESCRIPTION, asset.getDescription()); + + ManagementApiAsset apiObject = new ManagementApiAsset(); + apiObject.setProperties(properties); + return apiObject; + } + + private Policy mapPolicy(ManagementApiPolicy managementApiPolicy) { + String id = managementApiPolicy.uid; + List permissions = + managementApiPolicy.permissions.stream() + .map(this::mapPermission) + .collect(Collectors.toList()); + + return new Policy(id, permissions); + } - log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); - return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); - } + private ManagementApiPolicy mapPolicy(Policy policy) { + List permissions = + policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); + ManagementApiPolicy managementApiPolicy = new ManagementApiPolicy(); + managementApiPolicy.permissions = permissions; - private T get(String path, Header header, TypeToken typeToken) throws IOException { + return managementApiPolicy; + } + + private ManagementApiPolicyDefinition mapPolicyDefinition(Policy policy) { + ManagementApiPolicyDefinition apiObject = new ManagementApiPolicyDefinition(); + apiObject.id = policy.getId(); + apiObject.policy = mapPolicy(policy); + return apiObject; + } + + private Permission mapPermission(ManagementApiPermission managementApiPermission) { + String target = managementApiPermission.target; + String action = managementApiPermission.action.type; + return new Permission(action, new ArrayList<>(), target); + } + + private ManagementApiPermission mapPermission(Permission permission) { + String target = permission.getTarget(); + String action = permission.getAction(); + + ManagementApiRuleAction apiAction = new ManagementApiRuleAction(); + apiAction.type = action; + + var constraints = + permission.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); + + ManagementApiPermission apiObject = new ManagementApiPermission(); + apiObject.target = target; + apiObject.action = apiAction; + apiObject.constraints = constraints; + return apiObject; + } + + private ManagementConstraint mapConstraint(Constraint constraint) { + if (OrConstraint.class.equals(constraint.getClass())) { + return mapConstraint((OrConstraint) constraint); + } else if (BusinessPartnerNumberConstraint.class.equals(constraint.getClass())) { + return mapConstraint((BusinessPartnerNumberConstraint) constraint); + } else if (PayMeConstraint.class.equals(constraint.getClass())) { + return mapConstraint((PayMeConstraint) constraint); + } else { + throw new UnsupportedOperationException( + "Unsupported constraint type: " + constraint.getClass().getName()); + } + } - final HttpGet get = new HttpGet(path); - get.addHeader(header); - final HttpResponse response = sendRequest(get); - final byte[] json = response.getEntity().getContent().readAllBytes(); + private ManagementAtomicConstraint mapConstraint(PayMeConstraint constraint) { + ManagementApiLiteralExpression leftExpression = new ManagementApiLiteralExpression(); + leftExpression.value = "PayMe"; - log.debug("Received response: {}", new String(json, StandardCharsets.UTF_8)); - return new Gson().fromJson(new String(json, StandardCharsets.UTF_8), typeToken.getType()); - } + ManagementApiLiteralExpression rightExpression = new ManagementApiLiteralExpression(); + rightExpression.value = String.valueOf(constraint.getAmount()); - private void post(String path, Object object) throws IOException { - post(path, object, new TypeToken() {}); - } + ManagementAtomicConstraint dataManagementApiConstraint = new ManagementAtomicConstraint(); + dataManagementApiConstraint.leftExpression = leftExpression; + dataManagementApiConstraint.rightExpression = rightExpression; + dataManagementApiConstraint.operator = "EQ"; - private T post(String path, Object object, TypeToken typeToken) throws IOException { - final String url = String.format("%s%s", dataMgmtUrl, path); - final HttpPost post = new HttpPost(url); - post.addHeader("Content-Type", "application/json"); - - var json = new Gson().toJson(object); - - log.debug("POST Payload: " + json); - - post.setEntity(new StringEntity(json)); - final CloseableHttpResponse response = sendRequest(post); - - T responseJson = null; - if (!typeToken.equals(new TypeToken() {})) { - final byte[] responseBytes = response.getEntity().getContent().readAllBytes(); - responseJson = - new Gson() - .fromJson(new String(responseBytes, StandardCharsets.UTF_8), typeToken.getType()); - } - - response.close(); - - return responseJson; - } - - private CloseableHttpResponse sendRequest(HttpRequestBase request) throws IOException { - request.addHeader("X-Api-Key", dataMgmtAuthKey); - - log.debug(String.format("Send %-6s %s", request.getMethod(), request.getURI())); - - final CloseableHttpResponse response = httpClient.execute(request); - if (200 > response.getStatusLine().getStatusCode() - || response.getStatusLine().getStatusCode() >= 300) { - throw new RuntimeException( - String.format("Unexpected response: %s", response.getStatusLine())); - } - - return response; - } - - private ContractNegotiation mapNegotiation(ManagementApiNegotiation negotiation) { - - ContractNegotiationState state; - - switch (negotiation.state) { - case "ERROR": - state = ContractNegotiationState.ERROR; - break; - case "INITIAL": - state = ContractNegotiationState.INITIAL; - break; - case "DECLINED": - state = ContractNegotiationState.DECLINED; - break; - case "CONFIRMED": - state = ContractNegotiationState.CONFIRMED; - break; - default: - state = ContractNegotiationState.UNKNOWN; - } - - return new ContractNegotiation(negotiation.id, negotiation.contractAgreementId, state); - } - - private TransferProcess mapTransferProcess(ManagementApiTransferProcess transferProcess) { - - TransferProcessState state; - - switch (transferProcess.state) { - case "COMPLETED": - state = TransferProcessState.COMPLETED; - break; - case "ERROR": - state = TransferProcessState.ERROR; - break; - default: - state = TransferProcessState.UNKNOWN; - } - - return new TransferProcess(transferProcess.id, state); - } - - private ManagementApiDataAddress mapDataAddress(@NonNull DataAddress dataAddress) { - final ManagementApiDataAddress apiObject = new ManagementApiDataAddress(); - - if (dataAddress instanceof HttpProxySourceDataAddress) { - final var address = (HttpProxySourceDataAddress) dataAddress; - var properties = new HashMap(); - properties.put("type", "HttpData"); - properties.put("baseUrl", address.getBaseUrl()); - var oauth2Provision = address.getOauth2Provision(); - if (oauth2Provision != null) { - properties.put("oauth2:tokenUrl", oauth2Provision.getTokenUrl()); - properties.put("oauth2:clientId", oauth2Provision.getClientId()); - properties.put("oauth2:clientSecret", oauth2Provision.getClientSecret()); - properties.put("oauth2:scope", oauth2Provision.getScope()); - } - apiObject.setProperties(properties); - } else if (dataAddress instanceof HttpProxySinkDataAddress) { - apiObject.setProperties(Map.of("type", "HttpProxy")); - } else if (dataAddress instanceof S3DataAddress) { - final S3DataAddress a = (S3DataAddress) dataAddress; - apiObject.setProperties( - Map.of( - "type", - "AmazonS3", - "bucketName", - a.getBucketName(), - "region", - a.getRegion(), - "keyName", - a.getKeyName())); - } else if (dataAddress instanceof NullDataAddress) { - // set something that passes validation - apiObject.setProperties(Map.of("type", "HttpData", "baseUrl", "http://localhost")); - } else { - throw new UnsupportedOperationException( - String.format( - "Cannot map data address of type %s to EDC domain", dataAddress.getClass())); - } - - return apiObject; - } - - private ManagementApiAsset mapAsset(Asset asset) { - final Map properties = - Map.of( - ManagementApiAsset.ID, asset.getId(), - ManagementApiAsset.DESCRIPTION, asset.getDescription()); - - final ManagementApiAsset apiObject = new ManagementApiAsset(); - apiObject.setProperties(properties); - return apiObject; - } - - private Policy mapPolicy(ManagementApiPolicy managementApiPolicy) { - final String id = managementApiPolicy.uid; - final List permissions = - managementApiPolicy.permissions.stream() - .map(this::mapPermission) - .collect(Collectors.toList()); - - return new Policy(id, permissions); - } - - private ManagementApiPolicy mapPolicy(Policy policy) { - final List permissions = - policy.getPermission().stream().map(this::mapPermission).collect(Collectors.toList()); - final ManagementApiPolicy managementApiPolicy = new ManagementApiPolicy(); - managementApiPolicy.permissions = permissions; - - return managementApiPolicy; - } - - private ManagementApiPolicyDefinition mapPolicyDefinition(Policy policy) { - final ManagementApiPolicyDefinition apiObject = new ManagementApiPolicyDefinition(); - apiObject.id = policy.getId(); - apiObject.policy = mapPolicy(policy); - return apiObject; - } - - private Permission mapPermission(ManagementApiPermission managementApiPermission) { - final String target = managementApiPermission.target; - final String action = managementApiPermission.action.type; - return new Permission(action, target, new ArrayList<>()); - } - - private ManagementApiPermission mapPermission(Permission permission) { - final String target = permission.getTarget(); - final String action = permission.getAction(); - - final ManagementApiRuleAction apiAction = new ManagementApiRuleAction(); - apiAction.type = action; - - var constraints = - permission.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); - - final ManagementApiPermission apiObject = new ManagementApiPermission(); - apiObject.target = target; - apiObject.action = apiAction; - apiObject.constraints = constraints; - return apiObject; - } - - private ManagementConstraint mapConstraint(Constraint constraint) { - if (OrConstraint.class.equals(constraint.getClass())) { - return mapConstraint((OrConstraint) constraint); - } else if (BusinessPartnerNumberConstraint.class.equals(constraint.getClass())) { - return mapConstraint((BusinessPartnerNumberConstraint) constraint); - } else if (PayMeConstraint.class.equals(constraint.getClass())) { - return mapConstraint((PayMeConstraint) constraint); - } else { - throw new UnsupportedOperationException( - "Unsupported constraint type: " + constraint.getClass().getName()); - } - } - - private ManagementAtomicConstraint mapConstraint(PayMeConstraint constraint) { - final ManagementApiLiteralExpression leftExpression = new ManagementApiLiteralExpression(); - leftExpression.value = "PayMe"; - - final ManagementApiLiteralExpression rightExpression = new ManagementApiLiteralExpression(); - rightExpression.value = String.valueOf(constraint.getAmount()); - - final ManagementAtomicConstraint dataManagementApiConstraint = new ManagementAtomicConstraint(); - dataManagementApiConstraint.leftExpression = leftExpression; - dataManagementApiConstraint.rightExpression = rightExpression; - dataManagementApiConstraint.operator = "EQ"; - - return dataManagementApiConstraint; - } - - private ManagementAtomicConstraint mapConstraint(BusinessPartnerNumberConstraint constraint) { - final ManagementApiLiteralExpression leftExpression = new ManagementApiLiteralExpression(); - leftExpression.value = "BusinessPartnerNumber"; - - final ManagementApiLiteralExpression rightExpression = new ManagementApiLiteralExpression(); - rightExpression.value = constraint.getBusinessPartnerNumber(); - - final ManagementAtomicConstraint dataManagementApiConstraint = new ManagementAtomicConstraint(); - dataManagementApiConstraint.leftExpression = leftExpression; - dataManagementApiConstraint.rightExpression = rightExpression; - dataManagementApiConstraint.operator = "EQ"; - - return dataManagementApiConstraint; - } - - private ManagementOrConstraint mapConstraint(OrConstraint constraint) { - var orConstraint = new ManagementOrConstraint(); - orConstraint.constraints = - constraint.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); - return orConstraint; - } - - private ContractOffer mapOffer(ManagementApiContractOffer managementApiContractOffer) { - final String id = managementApiContractOffer.id; - final String assetId = - managementApiContractOffer.assetId != null - ? managementApiContractOffer.assetId - : (String) managementApiContractOffer.asset.getProperties().get(ManagementApiAsset.ID); - - final Policy policy = mapPolicy(managementApiContractOffer.getPolicy()); - - return new ContractOffer(id, policy, assetId); - } - - private ManagementApiContractDefinition mapContractDefinition( - ContractDefinition contractDefinition) { - - final ManagementApiContractDefinition apiObject = new ManagementApiContractDefinition(); - apiObject.id = contractDefinition.getId(); - apiObject.accessPolicyId = contractDefinition.getAcccessPolicyId(); - apiObject.contractPolicyId = contractDefinition.getContractPolicyId(); - apiObject.criteria = new ArrayList<>(); - - for (final String assetId : contractDefinition.getAssetIds()) { - ManagementApiCriterion criterion = new ManagementApiCriterion(); - criterion.operandLeft = ManagementApiAsset.ID; - criterion.operator = "="; - criterion.operandRight = assetId; - - apiObject.criteria.add(criterion); - } - - return apiObject; - } - - @Data - private static class ManagementApiNegotiationResponse { - private String id; - } - - @Data - private static class ManagementApiNegotiationPayload { - private String connectorId = "foo"; - private String connectorAddress; - private ManagementApiOffer offer; - } - - @Data - private static class ManagementApiNegotiation { - private String id; - private String state; - private String contractAgreementId; - } - - @Data - private static class ManagementApiTransferProcess { - private String id; - private String state; - } - - @Data - private static class ManagementApiOffer { - private String offerId; - private String assetId; - private ManagementApiPolicy policy; - } - - @Data - private static class ManagementApiTransfer { - private String connectorId = "foo"; - private String connectorAddress; - private String contractId; - private String assetId; - private String protocol; - private ManagementApiDataAddress dataDestination; - private boolean managedResources; - private ManagementApiTransferType transferType; - private ManagementApiProperties properties; - } - - @Data - private static class ManagementApiTransferType { - private String contentType = "application/octet-stream"; - private boolean isFinite = true; - } - - @Data - private static class ManagementApiTransferResponse { - private String id; - } - - @Data - private static class ManagementApiAssetCreate { - private ManagementApiAsset asset; - private ManagementApiDataAddress dataAddress; - } - - @Data - private static class ManagementApiAsset { - public static final String ID = "asset:prop:id"; - public static final String DESCRIPTION = "asset:prop:description"; - - private Map properties; - } - - @Data - private static class ManagementApiDataAddress { - public static final String TYPE = "type"; - private Map properties; - } - - @Data - private static class ManagementApiProperties { - @SerializedName(value = "receiver.http.endpoint") - private final String receiverHttpEndpoint; - } - - @Data - private static class ManagementApiPolicyDefinition { - private String id; - private ManagementApiPolicy policy; - } - - @Data - private static class ManagementApiPolicy { - private String uid; - private List permissions = new ArrayList<>(); - } - - @Data - private static class ManagementApiPermission { - private String edctype = "dataspaceconnector:permission"; - private ManagementApiRuleAction action; - private String target; - private List constraints = new ArrayList<>(); - } - - @Data - private static class ManagementAtomicConstraint implements ManagementConstraint { - private String edctype = "AtomicConstraint"; - private ManagementApiLiteralExpression leftExpression; - private ManagementApiLiteralExpression rightExpression; - private String operator; - } - - @Data - private static class ManagementOrConstraint implements ManagementConstraint { - private String edctype = "dataspaceconnector:orconstraint"; - private List constraints; - } - - private interface ManagementConstraint {} - - @Data - private static class ManagementApiLiteralExpression { - private String edctype = "dataspaceconnector:literalexpression"; - private String value; - } - - @Data - private static class ManagementApiRuleAction { - private String type; - } - - @Data - private static class ManagementApiContractDefinition { - private String id; - private String accessPolicyId; - private String contractPolicyId; - private List criteria = new ArrayList<>(); - } - - @Data - private static class ManagementApiCriterion { - private Object operandLeft; - private String operator; - private Object operandRight; - } - - @Data - private static class ManagementApiContractOffer { - private String id; - private ManagementApiPolicy policy; - private ManagementApiAsset asset; - private String assetId; - } - - @Data - private static class ManagementApiContractOfferCatalog { - private String id; - private List contractOffers = new ArrayList<>(); - } + return dataManagementApiConstraint; + } + + private ManagementAtomicConstraint mapConstraint(BusinessPartnerNumberConstraint constraint) { + ManagementApiLiteralExpression leftExpression = new ManagementApiLiteralExpression(); + leftExpression.value = "BusinessPartnerNumber"; + + ManagementApiLiteralExpression rightExpression = new ManagementApiLiteralExpression(); + rightExpression.value = constraint.getBusinessPartnerNumber(); + + ManagementAtomicConstraint dataManagementApiConstraint = new ManagementAtomicConstraint(); + dataManagementApiConstraint.leftExpression = leftExpression; + dataManagementApiConstraint.rightExpression = rightExpression; + dataManagementApiConstraint.operator = "EQ"; + + return dataManagementApiConstraint; + } + + private ManagementOrConstraint mapConstraint(OrConstraint constraint) { + var orConstraint = new ManagementOrConstraint(); + orConstraint.constraints = + constraint.getConstraints().stream().map(this::mapConstraint).collect(Collectors.toList()); + return orConstraint; + } + + private ContractOffer mapOffer(ManagementApiContractOffer managementApiContractOffer) { + String id = managementApiContractOffer.id; + String assetId = + managementApiContractOffer.assetId != null + ? managementApiContractOffer.assetId + : (String) managementApiContractOffer.asset.getProperties().get(ManagementApiAsset.ID); + + Policy policy = mapPolicy(managementApiContractOffer.getPolicy()); + + return new ContractOffer(id, policy, assetId); + } + + private ManagementApiContractDefinition mapContractDefinition( + ContractDefinition contractDefinition) { + + ManagementApiContractDefinition apiObject = new ManagementApiContractDefinition(); + apiObject.id = contractDefinition.getId(); + apiObject.accessPolicyId = contractDefinition.getAcccessPolicyId(); + apiObject.contractPolicyId = contractDefinition.getContractPolicyId(); + apiObject.criteria = new ArrayList<>(); + + for (String assetId : contractDefinition.getAssetIds()) { + ManagementApiCriterion criterion = new ManagementApiCriterion(); + criterion.operandLeft = ManagementApiAsset.ID; + criterion.operator = "="; + criterion.operandRight = assetId; + + apiObject.criteria.add(criterion); + } + + return apiObject; + } + + private interface ManagementConstraint { + } + + + private static class ManagementApiNegotiationResponse { + private String id; + + + public String getId() { + return id; + } + } + + + private static class ManagementApiNegotiationPayload { + private final String connectorId = "foo"; + private String connectorAddress; + private ManagementApiOffer offer; + } + + private static class ManagementApiNegotiation { + private String id; + private String state; + private String contractAgreementId; + } + + private static class ManagementApiTransferProcess { + private String id; + private String state; + } + + + private static class ManagementApiOffer { + private String offerId; + private String assetId; + private ManagementApiPolicy policy; + } + + + private static class ManagementApiTransfer { + private final String connectorId = "foo"; + private String connectorAddress; + private String contractId; + private String assetId; + private String protocol; + private ManagementApiDataAddress dataDestination; + private boolean managedResources; + private ManagementApiTransferType transferType; + private ManagementApiProperties properties; + } + + + private static class ManagementApiTransferType { + private final String contentType = "application/octet-stream"; + private final boolean isFinite = true; + } + + + private static class ManagementApiTransferResponse { + private String id; + + + public String getId() { + return id; + } + } + + private static class ManagementApiAssetCreate { + private ManagementApiAsset asset; + private ManagementApiDataAddress dataAddress; + } + + private static class ManagementApiAsset { + public static final String ID = "asset:prop:id"; + public static final String DESCRIPTION = "asset:prop:description"; + + private Map properties; + + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + + private static class ManagementApiDataAddress { + public static final String TYPE = "type"; + private Map properties; + + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + + private static class ManagementApiProperties { + @SerializedName(value = "receiver.http.endpoint") + private final String receiverHttpEndpoint; + + private ManagementApiProperties(String receiverHttpEndpoint) { + this.receiverHttpEndpoint = receiverHttpEndpoint; + } + } + + + private static class ManagementApiPolicyDefinition { + private String id; + private ManagementApiPolicy policy; + } + + + private static class ManagementApiPolicy { + private String uid; + private List permissions = new ArrayList<>(); + } + + + private static class ManagementApiPermission { + private final String edctype = "dataspaceconnector:permission"; + private ManagementApiRuleAction action; + private String target; + private List constraints = new ArrayList<>(); + } + + + private static class ManagementAtomicConstraint implements ManagementConstraint { + private final String edctype = "AtomicConstraint"; + private ManagementApiLiteralExpression leftExpression; + private ManagementApiLiteralExpression rightExpression; + private String operator; + } + + + private static class ManagementOrConstraint implements ManagementConstraint { + private final String edctype = "dataspaceconnector:orconstraint"; + private List constraints; + } + + + private static class ManagementApiLiteralExpression { + private final String edctype = "dataspaceconnector:literalexpression"; + private String value; + } + + + private static class ManagementApiRuleAction { + private String type; + } + + + private static class ManagementApiContractDefinition { + private String id; + private String accessPolicyId; + private String contractPolicyId; + private List criteria = new ArrayList<>(); + } + + + private static class ManagementApiCriterion { + private Object operandLeft; + private String operator; + private Object operandRight; + } + + + private static class ManagementApiContractOffer { + private String id; + private ManagementApiPolicy policy; + private ManagementApiAsset asset; + private String assetId; + + + public ManagementApiPolicy getPolicy() { + return policy; + } + } + + + private static class ManagementApiContractOfferCatalog { + private final List contractOffers = new ArrayList<>(); + private String id; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java index d1a199fd1..49a2353d1 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/Environment.java @@ -20,6 +20,9 @@ package org.eclipse.tractusx.edc.tests; +import java.util.Locale; +import java.util.Objects; + import static org.eclipse.tractusx.edc.tests.Constants.AWS_ACCESS_KEY_ID; import static org.eclipse.tractusx.edc.tests.Constants.AWS_SECRET_ACCESS_KEY; import static org.eclipse.tractusx.edc.tests.Constants.BACKEND_SERVICE_BACKEND_API_URL; @@ -32,45 +35,165 @@ import static org.eclipse.tractusx.edc.tests.Constants.EDC_AWS_ENDPOINT_OVERRIDE; import static org.eclipse.tractusx.edc.tests.Constants.IDS_URL; -import java.util.Locale; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; - -@Builder(access = AccessLevel.PRIVATE) -@Getter -@ToString public class Environment { - @NonNull private final String dataManagementAuthKey; - @NonNull private final String dataManagementUrl; - @NonNull private final String idsUrl; - @NonNull private final String dataPlaneUrl; - @NonNull private final String backendServiceBackendApiUrl; - @NonNull private final String databaseUrl; - @NonNull private final String databaseUser; - @NonNull private final String databasePassword; - @NonNull private final String awsEndpointOverride; - @NonNull private final String awsAccessKey; - @NonNull private final String awsSecretAccessKey; - - public static Environment byName(String name) { - name = name.toUpperCase(Locale.ROOT); - - return Environment.builder() - .dataManagementUrl(System.getenv(String.join("_", name, DATA_MANAGEMENT_URL))) - .dataManagementAuthKey(System.getenv(String.join("_", name, DATA_MANAGEMENT_API_AUTH_KEY))) - .idsUrl(System.getenv(String.join("_", name, IDS_URL))) - .dataPlaneUrl(System.getenv(String.join("_", name, DATA_PLANE_URL))) - .backendServiceBackendApiUrl( - System.getenv(String.join("_", name, BACKEND_SERVICE_BACKEND_API_URL))) - .databaseUrl(System.getenv(String.join("_", name, DATABASE_URL))) - .databaseUser(System.getenv(String.join("_", name, DATABASE_USER))) - .databasePassword(System.getenv(String.join("_", name, DATABASE_PASSWORD))) - .awsEndpointOverride(System.getenv(EDC_AWS_ENDPOINT_OVERRIDE)) - .awsAccessKey(System.getenv(String.join("_", name, AWS_ACCESS_KEY_ID))) - .awsSecretAccessKey(System.getenv(String.join("_", name, AWS_SECRET_ACCESS_KEY))) - .build(); - } + + private String awsEndpointOverride; + private String awsAccessKey; + private String awsSecretAccessKey; + private String dataManagementAuthKey; + private String dataManagementUrl; + private String idsUrl; + private String dataPlaneUrl; + private String backendServiceBackendApiUrl; + private String databaseUrl; + private String databaseUser; + private String databasePassword; + + private Environment() { + + } + + + public static Environment byName(String name) { + var upperName = name.toUpperCase(Locale.ROOT); + + return Environment.Builder.newInstance() + .dataManagementUrl(System.getenv(String.join("_", upperName, DATA_MANAGEMENT_URL))) + .dataManagementAuthKey(System.getenv(String.join("_", upperName, DATA_MANAGEMENT_API_AUTH_KEY))) + .idsUrl(System.getenv(String.join("_", upperName, IDS_URL))) + .dataPlaneUrl(System.getenv(String.join("_", upperName, DATA_PLANE_URL))) + .backendServiceBackendApiUrl( + System.getenv(String.join("_", upperName, BACKEND_SERVICE_BACKEND_API_URL))) + .databaseUrl(System.getenv(String.join("_", upperName, DATABASE_URL))) + .databaseUser(System.getenv(String.join("_", upperName, DATABASE_USER))) + .databasePassword(System.getenv(String.join("_", upperName, DATABASE_PASSWORD))) + .awsEndpointOverride(System.getenv(EDC_AWS_ENDPOINT_OVERRIDE)) + .awsAccessKey(System.getenv(String.join("_", upperName, AWS_ACCESS_KEY_ID))) + .awsSecretAccessKey(System.getenv(String.join("_", upperName, AWS_SECRET_ACCESS_KEY))) + .build(); + } + + public String getIdsUrl() { + return idsUrl; + } + + public String getAwsEndpointOverride() { + return awsEndpointOverride; + } + + public String getAwsSecretAccessKey() { + return awsSecretAccessKey; + } + + public String getAwsAccessKey() { + return awsAccessKey; + } + + public String getBackendServiceBackendApiUrl() { + return backendServiceBackendApiUrl; + } + + public String getDatabasePassword() { + return databasePassword; + } + + public String getDatabaseUrl() { + return databaseUrl; + } + + public String getDatabaseUser() { + return databaseUser; + } + + public String getDataManagementAuthKey() { + return dataManagementAuthKey; + } + + public String getDataManagementUrl() { + return dataManagementUrl; + } + + private static class Builder { + + + private final Environment environment; + + private Builder() { + environment = new Environment(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder awsEndpointOverride(String val) { + environment.awsEndpointOverride = val; + return this; + } + + public Builder awsAccessKey(String val) { + environment.awsAccessKey = val; + return this; + } + + public Builder awsSecretAccessKey(String val) { + environment.awsSecretAccessKey = val; + return this; + } + + public Builder dataManagementAuthKey(String val) { + environment.dataManagementAuthKey = val; + return this; + } + + public Builder dataManagementUrl(String val) { + environment.dataManagementUrl = val; + return this; + } + + public Builder idsUrl(String val) { + environment.idsUrl = val; + return this; + } + + public Builder dataPlaneUrl(String val) { + environment.dataPlaneUrl = val; + return this; + } + + public Builder backendServiceBackendApiUrl(String val) { + environment.backendServiceBackendApiUrl = val; + return this; + } + + public Builder databaseUrl(String val) { + environment.databaseUrl = val; + return this; + } + + public Builder databaseUser(String val) { + environment.databaseUser = val; + return this; + } + + public Builder databasePassword(String val) { + environment.databasePassword = val; + return this; + } + + public Environment build() { + Objects.requireNonNull(environment.awsAccessKey); + Objects.requireNonNull(environment.awsEndpointOverride); + Objects.requireNonNull(environment.awsSecretAccessKey); + Objects.requireNonNull(environment.backendServiceBackendApiUrl); + Objects.requireNonNull(environment.databaseUrl); + Objects.requireNonNull(environment.databasePassword); + Objects.requireNonNull(environment.databaseUser); + Objects.requireNonNull(environment.dataManagementUrl); + Objects.requireNonNull(environment.dataPlaneUrl); + Objects.requireNonNull(environment.dataManagementAuthKey); + Objects.requireNonNull(environment.idsUrl); + return environment; + } + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java index c0ae99a48..39a743ab5 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/HttpProxyTransferSteps.java @@ -4,96 +4,102 @@ import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; +import org.eclipse.tractusx.edc.tests.data.Asset; +import org.eclipse.tractusx.edc.tests.data.DataAddress; +import org.eclipse.tractusx.edc.tests.data.HttpProxySinkDataAddress; +import org.eclipse.tractusx.edc.tests.data.HttpProxySourceDataAddress; +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.time.Duration; import java.util.Arrays; import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.awaitility.Awaitility; -import org.eclipse.tractusx.edc.tests.data.*; -import org.junit.jupiter.api.Assertions; import static org.awaitility.Awaitility.await; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; -@Slf4j public class HttpProxyTransferSteps { - private static final String ID = "id"; - private static final String DESCRIPTION = "description"; - private static final String BASE_URL = "baseUrl"; - private static final String ASSET_ID = "asset id"; - private static final String RECEIVER_HTTP_ENDPOINT = "receiverHttpEndpoint"; - - @Given("'{connector}' has a http proxy assets") - public void hasAssets(Connector connector, DataTable table) throws Exception { - final DataManagementAPI api = connector.getDataManagementAPI(); - - for (var map : table.asMaps()) { - final String id = map.get(ID); - final String description = map.get(DESCRIPTION); - final String baseUrl = map.get(BASE_URL); - - var oauth2Provision = - Arrays.stream(Oauth2DataAddressFields.values()) - .map(it -> it.text) - .anyMatch(map::containsKey) - ? new HttpProxySourceDataAddress.Oauth2Provision( - map.get(Oauth2DataAddressFields.TOKEN_URL.text), - map.get(Oauth2DataAddressFields.CLIENT_ID.text), - map.get(Oauth2DataAddressFields.CLIENT_SECRET.text), - map.get(Oauth2DataAddressFields.SCOPE.text)) - : null; - - final DataAddress address = new HttpProxySourceDataAddress(baseUrl, oauth2Provision); - final Asset asset = new Asset(id, description, address); - - api.createAsset(asset); + private static final Logger log = LoggerFactory.getLogger(HttpProxyTransferSteps.class); + + private static final String ID = "id"; + private static final String DESCRIPTION = "description"; + private static final String BASE_URL = "baseUrl"; + private static final String ASSET_ID = "asset id"; + private static final String RECEIVER_HTTP_ENDPOINT = "receiverHttpEndpoint"; + + @Given("'{connector}' has a http proxy assets") + public void hasAssets(Connector connector, DataTable table) throws Exception { + final DataManagementAPI api = connector.getDataManagementAPI(); + + for (var map : table.asMaps()) { + final String id = map.get(ID); + final String description = map.get(DESCRIPTION); + final String baseUrl = map.get(BASE_URL); + + var oauth2Provision = + Arrays.stream(Oauth2DataAddressFields.values()) + .map(it -> it.text) + .anyMatch(map::containsKey) + ? new HttpProxySourceDataAddress.Oauth2Provision( + map.get(Oauth2DataAddressFields.TOKEN_URL.text), + map.get(Oauth2DataAddressFields.CLIENT_ID.text), + map.get(Oauth2DataAddressFields.CLIENT_SECRET.text), + map.get(Oauth2DataAddressFields.SCOPE.text)) + : null; + + final DataAddress address = new HttpProxySourceDataAddress(baseUrl, oauth2Provision); + final Asset asset = new Asset(id, description, address); + + api.createAsset(asset); + } + } + + @When("'{connector}' initiates HttpProxy transfer from '{connector}'") + public void sokratesInitiateHttpProxyTransferProcessFromPlato( + Connector consumer, Connector provider, DataTable dataTable) throws IOException { + var api = consumer.getDataManagementAPI(); + var receiverUrl = provider.getEnvironment().getIdsUrl() + "/data"; + + var negotiation = api.getNegotiations(); + var agreementId = negotiation.get(0).getAgreementId(); + var dataAddress = new HttpProxySinkDataAddress(); + + for (var map : dataTable.asMaps()) { + final String assetId = map.get(ASSET_ID); + final String receiverHttpEndpoint = map.get(RECEIVER_HTTP_ENDPOINT); + var transfer = api.initiateTransferProcess(receiverUrl, agreementId, assetId, dataAddress, receiverHttpEndpoint); + + transfer.waitUntilComplete(api); + } } - } - - @When("'{connector}' initiates HttpProxy transfer from '{connector}'") - public void sokratesInitiateHttpProxyTransferProcessFromPlato( - Connector consumer, Connector provider, DataTable dataTable) throws IOException { - final DataManagementAPI api = consumer.getDataManagementAPI(); - final String receiverUrl = provider.getEnvironment().getIdsUrl() + "/data"; - - final List negotiation = api.getNegotiations(); - final String agreementId = negotiation.get(0).getAgreementId(); - final DataAddress dataAddress = new HttpProxySinkDataAddress(); - - for (var map : dataTable.asMaps()) { - final String assetId = map.get(ASSET_ID); - final String receiverHttpEndpoint = map.get(RECEIVER_HTTP_ENDPOINT); - final Transfer transfer = - api.initiateTransferProcess( - receiverUrl, agreementId, assetId, dataAddress, receiverHttpEndpoint); - - transfer.waitUntilComplete(api); + + @Then("the backend application of '{connector}' has received data") + public void theBackendApplicationOfSocratesHasReceivedData(Connector consumer) { + var api = consumer.getBackendServiceBackendAPI(); + when(api.list(eq("/"))).thenReturn(List.of("item1", "item2")); + await() + .atMost(Duration.ofSeconds(20)) + .pollInterval(Duration.ofSeconds(1)) + .untilAsserted(() -> { + final List transferredData = api.list("/"); + Assertions.assertNotEquals(0, transferredData.size()); + }); } - } - - @Then("the backend application of '{connector}' has received data") - public void theBackendApplicationOfSocratesHasReceivedData(Connector consumer) { - final BackendServiceBackendAPI api = consumer.getBackendServiceBackendAPI(); - await() - .atMost(Duration.ofSeconds(20)) - .pollInterval(Duration.ofSeconds(1)) - .untilAsserted(() ->{ - final List transferredData = api.list("/"); - Assertions.assertNotEquals(0, transferredData.size()); - }); - } - - private enum Oauth2DataAddressFields { - TOKEN_URL("oauth2 token url"), - CLIENT_ID("oauth2 client id"), - CLIENT_SECRET("oauth2 client secret"), - SCOPE("oauth2 scope"); - - private final String text; - - Oauth2DataAddressFields(String text) { - this.text = text; + + private enum Oauth2DataAddressFields { + TOKEN_URL("oauth2 token url"), + CLIENT_ID("oauth2 client id"), + CLIENT_SECRET("oauth2 client secret"), + SCOPE("oauth2 scope"); + + private final String text; + + Oauth2DataAddressFields(String text) { + this.text = text; + } } - } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java index 5872d2dfe..7a713ff1b 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/NegotiationSteps.java @@ -23,11 +23,6 @@ import io.cucumber.datatable.DataTable; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.edc.tests.data.ContractNegotiation; import org.eclipse.tractusx.edc.tests.data.ContractNegotiationState; import org.eclipse.tractusx.edc.tests.data.Negotiation; @@ -35,60 +30,66 @@ import org.eclipse.tractusx.edc.tests.data.Policy; import org.junit.jupiter.api.Assertions; -@Slf4j +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + public class NegotiationSteps { - private static final String DEFINITION_ID = "definition id"; - private static final String ASSET_ID = "asset id"; - private ContractNegotiation lastInitiatedNegotiation; + private static final String DEFINITION_ID = "definition id"; + private static final String ASSET_ID = "asset id"; - @When("'{connector}' sends '{connector}' an offer without constraints") - public void sendAnOfferWithoutConstraints(Connector sender, Connector receiver, DataTable table) - throws IOException { + private ContractNegotiation lastInitiatedNegotiation; - final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); - final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + @When("'{connector}' sends '{connector}' an offer without constraints") + public void sendAnOfferWithoutConstraints(Connector sender, Connector receiver, DataTable table) + throws IOException { - for (Map map : table.asMaps()) { - final String definitionId = map.get(DEFINITION_ID); - final String assetId = map.get(ASSET_ID); + DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - final Permission permission = new Permission("USE", null, new ArrayList<>()); - final Policy policy = new Policy("foo", List.of(permission)); + for (Map map : table.asMaps()) { + String definitionId = map.get(DEFINITION_ID); + String assetId = map.get(ASSET_ID); - final Negotiation negotiation = - dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); + Permission permission = new Permission("USE", new ArrayList<>(), null); + Policy policy = new Policy("foo", List.of(permission)); - // wait for negotiation to complete - negotiation.waitUntilComplete(dataManagementAPI); + Negotiation negotiation = + dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); - lastInitiatedNegotiation = dataManagementAPI.getNegotiation(negotiation.getId()); + // wait for negotiation to complete + negotiation.waitUntilComplete(dataManagementAPI); + + lastInitiatedNegotiation = dataManagementAPI.getNegotiation(negotiation.getId()); + } } - } - @When("'{connector}' successfully negotiation a contract agreement with '{connector}'") - public void sokratesSuccessfullyNegotiationAContractAgreementPlatoFor( - Connector consumer, Connector provider, DataTable table) throws IOException { - final DataManagementAPI api = consumer.getDataManagementAPI(); + @When("'{connector}' successfully negotiation a contract agreement with '{connector}'") + public void sokratesSuccessfullyNegotiationAContractAgreementPlatoFor( + Connector consumer, Connector provider, DataTable table) throws IOException { + DataManagementAPI api = consumer.getDataManagementAPI(); - final Map map = table.asMap(); - final String definitionId = map.get(DEFINITION_ID); - final String assetId = map.get(ASSET_ID); + Map map = table.asMap(); + String definitionId = map.get(DEFINITION_ID); + String assetId = map.get(ASSET_ID); - // as default always the "allow all" policy is used. So we can assume this here, too. - final Permission permission = new Permission("USE", null, new ArrayList<>()); - final Policy policy = new Policy("policy-id", List.of(permission)); + // as default always the "allow all" policy is used. So we can assume this here, too. + Permission permission = new Permission("USE", new ArrayList<>(), null); + Policy policy = new Policy("policy-id", List.of(permission)); - final String receiverUrl = provider.getEnvironment().getIdsUrl(); - final Negotiation negotiation = - api.initiateNegotiation(receiverUrl, assetId, definitionId, policy); + String receiverUrl = provider.getEnvironment().getIdsUrl(); + Negotiation negotiation = + api.initiateNegotiation(receiverUrl, assetId, definitionId, policy); - negotiation.waitUntilComplete(api); - } + negotiation.waitUntilComplete(api); + } - @Then("the negotiation is declined") - public void assertLastNegotiationDeclined() { - Assertions.assertEquals(ContractNegotiationState.DECLINED, lastInitiatedNegotiation.getState()); - } + @Then("the negotiation is declined") + public void assertLastNegotiationDeclined() { + Assertions.assertEquals(ContractNegotiationState.DECLINED, lastInitiatedNegotiation.getState()); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java index a7ede22be..d8bac4466 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/PolicyStepDefs.java @@ -20,45 +20,54 @@ package org.eclipse.tractusx.edc.tests; -import static java.util.Arrays.stream; -import static java.util.stream.Collectors.toList; - import io.cucumber.datatable.DataTable; import io.cucumber.java.en.Given; +import org.eclipse.tractusx.edc.tests.data.BusinessPartnerNumberConstraint; +import org.eclipse.tractusx.edc.tests.data.Constraint; +import org.eclipse.tractusx.edc.tests.data.OrConstraint; +import org.eclipse.tractusx.edc.tests.data.PayMeConstraint; +import org.eclipse.tractusx.edc.tests.data.Permission; +import org.eclipse.tractusx.edc.tests.data.Policy; + import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.eclipse.tractusx.edc.tests.data.*; + +import static java.util.Arrays.stream; +import static java.util.stream.Collectors.toList; public class PolicyStepDefs { - @Given("'{connector}' has the following policies") - public void hasPolicies(Connector connector, DataTable table) throws Exception { - var api = connector.getDataManagementAPI(); - var policies = table.asMaps().stream().map(this::parseRow).collect(toList()); + @Given("'{connector}' has the following policies") + public void hasPolicies(Connector connector, DataTable table) throws Exception { + var api = connector.getDataManagementAPI(); + var policies = table.asMaps().stream().map(this::parseRow).collect(toList()); - for (var policy : policies) api.createPolicy(policy); - } + for (var policy : policies) { + api.createPolicy(policy); + } + } - private Policy parseRow(Map row) { - var id = row.get("id"); - var action = row.get("action"); - var constraints = new ArrayList(); + private Policy parseRow(Map row) { + var id = row.get("id"); + var action = row.get("action"); + var constraints = new ArrayList(); - var businessPartnerNumber = row.get("businessPartnerNumber"); - if (businessPartnerNumber != null && !businessPartnerNumber.isBlank()) { - var bpnConstraints = - stream(businessPartnerNumber.split(",")) - .map(BusinessPartnerNumberConstraint::new) - .collect(toList()); - constraints.add(new OrConstraint(bpnConstraints)); - } + var businessPartnerNumber = row.get("businessPartnerNumber"); + if (businessPartnerNumber != null && !businessPartnerNumber.isBlank()) { + var bpnConstraints = + stream(businessPartnerNumber.split(",")) + .map(BusinessPartnerNumberConstraint::new) + .collect(toList()); + constraints.add(new OrConstraint(bpnConstraints)); + } - var payMe = row.get("payMe"); - if (payMe != null && !payMe.isBlank()) - constraints.add(new PayMeConstraint(Double.parseDouble(payMe))); + var payMe = row.get("payMe"); + if (payMe != null && !payMe.isBlank()) { + constraints.add(new PayMeConstraint(Double.parseDouble(payMe))); + } - var permission = new Permission(action, null, constraints); - return new Policy(id, List.of(permission)); - } + var permission = new Permission(action, constraints, null); + return new Policy(id, List.of(permission)); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java index c4bc85a27..05f5f1242 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/S3FileTransferStepsDefs.java @@ -19,21 +19,10 @@ package org.eclipse.tractusx.edc.tests; -import static org.awaitility.Awaitility.await; -import static org.junit.jupiter.api.Assertions.fail; - import io.cucumber.datatable.DataTable; import io.cucumber.java.AfterAll; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; import org.eclipse.tractusx.edc.tests.data.Asset; import org.eclipse.tractusx.edc.tests.data.DataAddress; import org.eclipse.tractusx.edc.tests.data.Negotiation; @@ -45,135 +34,145 @@ import org.eclipse.tractusx.edc.tests.util.Timeouts; import org.junit.jupiter.api.Assertions; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.fail; + public class S3FileTransferStepsDefs { - @Given("'{connector}' has an empty storage bucket called {string}") - public void hasEmptyStorageBucket(Connector connector, String bucketName) { - S3Client s3 = connector.getS3Client(); + private static final String COMPLETION_MARKER = ".complete"; + private File fileToTransfer; + private String assetId; + private String agreementId; - s3.createBucket(bucketName); + @AfterAll + public static void bucketsCleanup() { + S3Client s3 = new S3Client(Environment.byName("Sokrates")); + s3.deleteAllBuckets(); + } - Assertions.assertTrue(s3.listBuckets().contains(bucketName)); - Assertions.assertEquals(0, s3.listBucketContent(bucketName).size()); - } + @Given("'{connector}' has an empty storage bucket called {string}") + public void hasEmptyStorageBucket(Connector connector, String bucketName) { + S3Client s3 = connector.getS3Client(); - private File fileToTransfer; + s3.createBucket(bucketName); - @Given("'{connector}' has a storage bucket called {string} with the file called {string}") - public void hasAStorageBucketWithTheFile(Connector connector, String bucketName, String fileName) - throws IOException { + Assertions.assertTrue(s3.listBuckets().contains(bucketName)); + Assertions.assertEquals(0, s3.listBucketContent(bucketName).size()); + } - S3Client s3 = connector.getS3Client(); - s3.createBucket(bucketName); - fileToTransfer = s3.uploadFile(bucketName, fileName); + @Given("'{connector}' has a storage bucket called {string} with the file called {string}") + public void hasAStorageBucketWithTheFile(Connector connector, String bucketName, String fileName) + throws IOException { - Set bucketContent = s3.listBucketContent(bucketName); + S3Client s3 = connector.getS3Client(); + s3.createBucket(bucketName); + fileToTransfer = s3.uploadFile(bucketName, fileName); - Assertions.assertEquals(1, bucketContent.size()); - Assertions.assertTrue(bucketContent.contains(fileName)); - } + Set bucketContent = s3.listBucketContent(bucketName); - @Given("'{connector}' has the following S3 assets") - public void hasAssets(Connector connector, DataTable table) { - final DataManagementAPI api = connector.getDataManagementAPI(); + Assertions.assertEquals(1, bucketContent.size()); + Assertions.assertTrue(bucketContent.contains(fileName)); + } - parseDataTable(table) - .forEach( - asset -> { - try { - api.createAsset(asset); - } catch (IOException e) { - fail(e.getMessage()); - } - }); - } + @Given("'{connector}' has the following S3 assets") + public void hasAssets(Connector connector, DataTable table) { + DataManagementAPI api = connector.getDataManagementAPI(); + + parseDataTable(table) + .forEach( + asset -> { + try { + api.createAsset(asset); + } catch (IOException e) { + fail(e.getMessage()); + } + }); + } - private String assetId; - private String agreementId; + @Then("'{connector}' negotiates the contract successfully with '{connector}'") + public void negotiateContract(Connector sender, Connector receiver, DataTable dataTable) + throws IOException { - @Then("'{connector}' negotiates the contract successfully with '{connector}'") - public void negotiateContract(Connector sender, Connector receiver, DataTable dataTable) - throws IOException { + String definitionId = dataTable.asMaps().get(0).get("contract offer id"); + assetId = dataTable.asMaps().get(0).get("asset id"); + String policyId = dataTable.asMaps().get(0).get("policy id"); - String definitionId = dataTable.asMaps().get(0).get("contract offer id"); - assetId = dataTable.asMaps().get(0).get("asset id"); - String policyId = dataTable.asMaps().get(0).get("policy id"); + Policy policy = + new Policy(policyId, List.of(new Permission("USE", new ArrayList<>(), null))); - final Policy policy = - new Policy(policyId, List.of(new Permission("USE", null, new ArrayList<>()))); + DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); - final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + Negotiation negotiation = + dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); + negotiation.waitUntilComplete(dataManagementAPI); - final Negotiation negotiation = - dataManagementAPI.initiateNegotiation(receiverIdsUrl, definitionId, assetId, policy); - negotiation.waitUntilComplete(dataManagementAPI); + agreementId = dataManagementAPI.getNegotiation(negotiation.getId()).getAgreementId(); + } - agreementId = dataManagementAPI.getNegotiation(negotiation.getId()).getAgreementId(); - } + @Then("'{connector}' initiate S3 transfer process from '{connector}'") + public void initiateTransferProcess(Connector sender, Connector receiver, DataTable dataTable) + throws IOException { + DataAddress dataAddress = createDataAddress(dataTable.asMaps().get(0)); - @Then("'{connector}' initiate S3 transfer process from '{connector}'") - public void initiateTransferProcess(Connector sender, Connector receiver, DataTable dataTable) - throws IOException { - DataAddress dataAddress = createDataAddress(dataTable.asMaps().get(0)); + DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); + String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; - final DataManagementAPI dataManagementAPI = sender.getDataManagementAPI(); - final String receiverIdsUrl = receiver.getEnvironment().getIdsUrl() + "/data"; + Transfer transferProcess = + dataManagementAPI.initiateTransferProcess( + receiverIdsUrl, agreementId, assetId, dataAddress); + transferProcess.waitUntilComplete(dataManagementAPI); - final Transfer transferProcess = - dataManagementAPI.initiateTransferProcess( - receiverIdsUrl, agreementId, assetId, dataAddress); - transferProcess.waitUntilComplete(dataManagementAPI); + Assertions.assertNotNull(transferProcess.getId()); + } - Assertions.assertNotNull(transferProcess.getId()); - } + @Then("'{connector}' has a storage bucket called {string} with transferred file called {string}") + public void consumerHasAStorageBucketWithFileTransferred( + Connector connector, String bucketName, String fileName) throws IOException { + S3Client s3 = connector.getS3Client(); + await() + .pollDelay(Duration.ofMillis(500)) + .atMost(Timeouts.FILE_TRANSFER) + .until(() -> isFilePresent(s3, bucketName, fileName + COMPLETION_MARKER)); + + Set bucketContent = s3.listBucketContent(bucketName); + + Assertions.assertEquals(2, bucketContent.size()); + Assertions.assertTrue(bucketContent.contains(fileName)); + Assertions.assertArrayEquals( + Files.readAllBytes(fileToTransfer.toPath()), + Files.readAllBytes(s3.downloadFile(bucketName, fileName).toPath())); + } - private static final String COMPLETION_MARKER = ".complete"; + private boolean isFilePresent(S3Client s3, String bucketName, String fileName) { + return s3.listBucketContent(bucketName).contains(fileName); + } - @Then("'{connector}' has a storage bucket called {string} with transferred file called {string}") - public void consumerHasAStorageBucketWithFileTransferred( - Connector connector, String bucketName, String fileName) throws IOException { - S3Client s3 = connector.getS3Client(); - await() - .pollDelay(Duration.ofMillis(500)) - .atMost(Timeouts.FILE_TRANSFER) - .until(() -> isFilePresent(s3, bucketName, fileName + COMPLETION_MARKER)); + private List parseDataTable(DataTable table) { + List assetsWithDataAddresses = new ArrayList<>(); - Set bucketContent = s3.listBucketContent(bucketName); + for (Map map : table.asMaps()) { + String id = map.get("id"); + String description = map.get("description"); + assetsWithDataAddresses.add(new Asset(id, description, createDataAddress(map))); + } - Assertions.assertEquals(2, bucketContent.size()); - Assertions.assertTrue(bucketContent.contains(fileName)); - Assertions.assertArrayEquals( - Files.readAllBytes(fileToTransfer.toPath()), - Files.readAllBytes(s3.downloadFile(bucketName, fileName).toPath())); - } - - private boolean isFilePresent(S3Client s3, String bucketName, String fileName) { - return s3.listBucketContent(bucketName).contains(fileName); - } - - private List parseDataTable(DataTable table) { - final List assetsWithDataAddresses = new ArrayList<>(); - - for (Map map : table.asMaps()) { - String id = map.get("id"); - String description = map.get("description"); - assetsWithDataAddresses.add(new Asset(id, description, createDataAddress(map))); + return assetsWithDataAddresses; } - return assetsWithDataAddresses; - } - - private DataAddress createDataAddress(Map map) { - final String bucketName = map.get("data_address_s3_bucket_name"); - final String region = map.get("data_address_s3_region"); - final String keyName = map.get("data_address_s3_key_name"); - return new S3DataAddress(bucketName, region, keyName); - } - - @AfterAll - public static void bucketsCleanup() { - S3Client s3 = new S3Client(Environment.byName("Sokrates")); - s3.deleteAllBuckets(); - } + private DataAddress createDataAddress(Map map) { + String bucketName = map.get("data_address_s3_bucket_name"); + String region = map.get("data_address_s3_region"); + String keyName = map.get("data_address_s3_key_name"); + return new S3DataAddress(bucketName, region, keyName); + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java index acccef8d8..47142d0e9 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Asset.java @@ -19,14 +19,28 @@ */ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class Asset { - @NonNull String Id; + private final String id; + private final String description; + private final DataAddress dataAddress; - @NonNull String description; + public Asset(String id, String description, DataAddress dataAddress) { + this.id = Objects.requireNonNull(id); + this.description = Objects.requireNonNull(description); + this.dataAddress = Objects.requireNonNull(dataAddress); + } - @NonNull DataAddress dataAddress; + public String getId() { + return id; + } + + public String getDescription() { + return description; + } + + public DataAddress getDataAddress() { + return dataAddress; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java index b9c64d158..f276b3d34 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/BusinessPartnerNumberConstraint.java @@ -1,10 +1,16 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class BusinessPartnerNumberConstraint implements Constraint { - @NonNull String businessPartnerNumber; + private final String businessPartnerNumber; + + public BusinessPartnerNumberConstraint(String businessPartnerNumber) { + this.businessPartnerNumber = Objects.requireNonNull(businessPartnerNumber); + } + + public String getBusinessPartnerNumber() { + return businessPartnerNumber; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java index a9fca04a1..c90fe1788 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractDefinition.java @@ -20,17 +20,42 @@ package org.eclipse.tractusx.edc.tests.data; import java.util.List; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class ContractDefinition { - @NonNull String id; + private final String id; + + private final String contractPolicyId; + private final String acccessPolicyId; + + private final List assetIds; + private final Long validity; + + public ContractDefinition(String id, String contractPolicyId, String acccessPolicyId, List assetIds, Long validity) { + this.id = Objects.requireNonNull(id); + this.contractPolicyId = Objects.requireNonNull(contractPolicyId); + this.acccessPolicyId = Objects.requireNonNull(acccessPolicyId); + this.assetIds = assetIds; + this.validity = validity; + } + + public String getId() { + return id; + } + + public String getContractPolicyId() { + return contractPolicyId; + } + + public String getAcccessPolicyId() { + return acccessPolicyId; + } + + public List getAssetIds() { + return assetIds; + } - @NonNull String contractPolicyId; - @NonNull String acccessPolicyId; - List assetIds; - Long validity; } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java index 109249744..67f9dafb0 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractNegotiation.java @@ -20,12 +20,29 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class ContractNegotiation { - @NonNull String id; - String agreementId; - @NonNull ContractNegotiationState state; + private final String id; + private final ContractNegotiationState state; + private final String agreementId; + + + public ContractNegotiation(String id, ContractNegotiationState state, String agreementId) { + this.id = Objects.requireNonNull(id); + this.state = Objects.requireNonNull(state); + this.agreementId = agreementId; + } + + public String getId() { + return id; + } + + public ContractNegotiationState getState() { + return state; + } + + public String getAgreementId() { + return agreementId; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java index 75dfd8d27..7ac87cb9a 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/ContractOffer.java @@ -19,12 +19,28 @@ */ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class ContractOffer { - @NonNull String id; - Policy policy; - String assetId; + private final String id; + private final Policy policy; + private final String assetId; + + public ContractOffer(String id, Policy policy, String assetId) { + this.id = Objects.requireNonNull(id); + this.policy = policy; + this.assetId = assetId; + } + + public String getId() { + return id; + } + + public Policy getPolicy() { + return policy; + } + + public String getAssetId() { + return assetId; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/HttpProxySourceDataAddress.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/HttpProxySourceDataAddress.java index 4a5946cc9..97f300611 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/HttpProxySourceDataAddress.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/HttpProxySourceDataAddress.java @@ -1,18 +1,52 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class HttpProxySourceDataAddress implements DataAddress { - @NonNull String baseUrl; - Oauth2Provision oauth2Provision; - - @Value - public static class Oauth2Provision { - @NonNull String tokenUrl; - @NonNull String clientId; - @NonNull String clientSecret; - String scope; - } + private final String baseUrl; + private final Oauth2Provision oauth2Provision; + + public HttpProxySourceDataAddress(String baseUrl, Oauth2Provision oauth2Provision) { + this.baseUrl = Objects.requireNonNull(baseUrl); + this.oauth2Provision = oauth2Provision; + } + + public String getBaseUrl() { + return baseUrl; + } + + public Oauth2Provision getOauth2Provision() { + return oauth2Provision; + } + + public static class Oauth2Provision { + private final String tokenUrl; + private final String clientId; + private final String clientSecret; + private final String scope; + + public Oauth2Provision(String tokenUrl, String clientId, String clientSecret, String scope) { + this.tokenUrl = Objects.requireNonNull(tokenUrl); + this.clientId = Objects.requireNonNull(clientId); + this.clientSecret = Objects.requireNonNull(clientSecret); + this.scope = scope; + } + + public String getTokenUrl() { + return tokenUrl; + } + + public String getScope() { + return scope; + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java index 40845e4c0..0d380685e 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Negotiation.java @@ -1,34 +1,43 @@ package org.eclipse.tractusx.edc.tests.data; -import static org.awaitility.Awaitility.await; +import org.eclipse.tractusx.edc.tests.DataManagementAPI; +import org.eclipse.tractusx.edc.tests.util.Timeouts; -import groovyjarjarantlr4.v4.runtime.misc.NotNull; import java.io.IOException; import java.time.Duration; +import java.util.Objects; import java.util.stream.Stream; -import lombok.Value; -import org.eclipse.tractusx.edc.tests.DataManagementAPI; -import org.eclipse.tractusx.edc.tests.util.Timeouts; -@Value +import static org.awaitility.Awaitility.await; + + public class Negotiation { - @NotNull String id; - - public void waitUntilComplete(DataManagementAPI dataManagementAPI) { - await() - .pollDelay(Duration.ofMillis(2000)) - .atMost(Timeouts.CONTRACT_NEGOTIATION) - .until(() -> isComplete(dataManagementAPI)); - } - - public boolean isComplete(DataManagementAPI dataManagementAPI) throws IOException { - var negotiation = dataManagementAPI.getNegotiation(id); - return negotiation != null - && Stream.of( - ContractNegotiationState.ERROR, - ContractNegotiationState.CONFIRMED, - ContractNegotiationState.DECLINED) - .anyMatch((l) -> l.equals(negotiation.getState())); - } + + private final String id; + + public Negotiation(String id) { + this.id = Objects.requireNonNull(id); + } + + public void waitUntilComplete(DataManagementAPI dataManagementAPI) { + await() + .pollDelay(Duration.ofMillis(5000)) + .atMost(Timeouts.CONTRACT_NEGOTIATION) + .until(() -> isComplete(dataManagementAPI)); + } + + public boolean isComplete(DataManagementAPI dataManagementAPI) throws IOException { + var negotiation = dataManagementAPI.getNegotiation(id); + return negotiation != null + && Stream.of( + ContractNegotiationState.ERROR, + ContractNegotiationState.CONFIRMED, + ContractNegotiationState.DECLINED) + .anyMatch((l) -> l.equals(negotiation.getState())); + } + + public String getId() { + return id; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/OrConstraint.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/OrConstraint.java index 88bf3438d..66ffd4396 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/OrConstraint.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/OrConstraint.java @@ -1,11 +1,18 @@ package org.eclipse.tractusx.edc.tests.data; import java.util.List; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class OrConstraint implements Constraint { - @NonNull List constraints; + private final List constraints; + + public OrConstraint(List constraints) { + this.constraints = Objects.requireNonNull(constraints); + } + + public List getConstraints() { + return constraints; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java index 4376346b2..1412b8d76 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/PayMeConstraint.java @@ -20,13 +20,19 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.Value; - /** * The PayMe constraint should be used when no constraint validation/enforcement in the EDC is * intended. */ -@Value + public class PayMeConstraint implements Constraint { - double amount; + private final double amount; + + public PayMeConstraint(double amount) { + this.amount = amount; + } + + public double getAmount() { + return amount; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java index 128e6686f..e90cbfaf0 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Permission.java @@ -20,13 +20,30 @@ package org.eclipse.tractusx.edc.tests.data; import java.util.List; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class Permission { - @NonNull String action; - String target; + private final String action; + private final List constraints; + private final String target; + + + public Permission(String action, List constraints, String target) { + this.action = Objects.requireNonNull(action); + this.constraints = Objects.requireNonNull(constraints); + this.target = target; + } + + public String getAction() { + return action; + } + + public List getConstraints() { + return constraints; + } - @NonNull List constraints; + public String getTarget() { + return target; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java index 27ea65d7a..c58c79206 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Policy.java @@ -21,11 +21,23 @@ package org.eclipse.tractusx.edc.tests.data; import java.util.List; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; + -@Value public class Policy { - String id; - @NonNull List Permission; + private final String id; + private final List Permission; + + public Policy(String id, List permission) { + this.id = id; + Permission = Objects.requireNonNull(permission); + } + + public String getId() { + return id; + } + + public List getPermission() { + return Permission; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/S3DataAddress.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/S3DataAddress.java index a59843447..93fe5ce8b 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/S3DataAddress.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/S3DataAddress.java @@ -1,12 +1,28 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class S3DataAddress implements DataAddress { - @NonNull String bucketName; - @NonNull String region; - @NonNull String keyName; + private final String bucketName; + private final String region; + private final String keyName; + + public S3DataAddress(String bucketName, String region, String keyName) { + this.bucketName = Objects.requireNonNull(bucketName); + this.region = Objects.requireNonNull(region); + this.keyName = Objects.requireNonNull(keyName); + } + + public String getBucketName() { + return bucketName; + } + + public String getRegion() { + return region; + } + + public String getKeyName() { + return keyName; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Transfer.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Transfer.java index ebd722d07..ea38442a3 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Transfer.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/Transfer.java @@ -1,31 +1,41 @@ package org.eclipse.tractusx.edc.tests.data; -import static org.awaitility.Awaitility.await; +import org.eclipse.tractusx.edc.tests.DataManagementAPI; +import org.eclipse.tractusx.edc.tests.util.Timeouts; import java.io.IOException; import java.time.Duration; -import lombok.Value; -import org.eclipse.tractusx.edc.tests.DataManagementAPI; -import org.eclipse.tractusx.edc.tests.util.Timeouts; -@Value +import static org.awaitility.Awaitility.await; + + public class Transfer { - String id; + private final String id; + + public Transfer(String id) { + this.id = id; + } + + public void waitUntilComplete(DataManagementAPI dataManagementAPI) { + await() + .pollDelay(Duration.ofMillis(2000)) + .atMost(Timeouts.FILE_TRANSFER) + .until(() -> isComplete(dataManagementAPI)); + } - public void waitUntilComplete(DataManagementAPI dataManagementAPI) { - await() - .pollDelay(Duration.ofMillis(2000)) - .atMost(Timeouts.FILE_TRANSFER) - .until(() -> isComplete(dataManagementAPI)); - } + public boolean isComplete(DataManagementAPI dataManagementAPI) throws IOException { + var transferProcess = dataManagementAPI.getTransferProcess(id); + if (transferProcess == null) { + return false; + } - public boolean isComplete(DataManagementAPI dataManagementAPI) throws IOException { - var transferProcess = dataManagementAPI.getTransferProcess(id); - if (transferProcess == null) return false; + var state = transferProcess.getState(); - var state = transferProcess.getState(); + return state == TransferProcessState.COMPLETED || state == TransferProcessState.ERROR; + } - return state == TransferProcessState.COMPLETED || state == TransferProcessState.ERROR; - } + public String getId() { + return id; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java index 28be5157a..1c00e86c3 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/data/TransferProcess.java @@ -19,11 +19,22 @@ package org.eclipse.tractusx.edc.tests.data; -import lombok.NonNull; -import lombok.Value; +import java.util.Objects; -@Value public class TransferProcess { - @NonNull String id; - @NonNull TransferProcessState state; + private final String id; + private final TransferProcessState state; + + public TransferProcess(String id, TransferProcessState state) { + this.id = Objects.requireNonNull(id); + this.state = Objects.requireNonNull(state); + } + + public String getId() { + return id; + } + + public TransferProcessState getState() { + return state; + } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java index 53aececa9..e03f38e98 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/DatabaseCleaner.java @@ -24,28 +24,33 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; -import lombok.RequiredArgsConstructor; -@RequiredArgsConstructor + public class DatabaseCleaner { - private static final String SQL = - "DELETE FROM edc_contract_negotiation;\n" - + "DELETE FROM edc_contract_agreement;\n" - + "DELETE FROM edc_transfer_process;\n" - + "DELETE FROM edc_contract_definitions;\n" - + "DELETE FROM edc_policydefinitions;\n" - + "DELETE FROM edc_asset;\n" - + "DELETE FROM edc_lease;"; - - private final String url; - private final String user; - private final String password; - - public void run() throws SQLException { - try (Connection con = DriverManager.getConnection(url, user, password)) { - Statement st = con.createStatement(); - st.executeUpdate(SQL); + private static final String SQL = + "DELETE FROM edc_contract_negotiation;\n" + + "DELETE FROM edc_contract_agreement;\n" + + "DELETE FROM edc_transfer_process;\n" + + "DELETE FROM edc_contract_definitions;\n" + + "DELETE FROM edc_policydefinitions;\n" + + "DELETE FROM edc_asset;\n" + + "DELETE FROM edc_lease;"; + + private final String url; + private final String user; + private final String password; + + public DatabaseCleaner(String url, String user, String password) { + this.url = url; + this.user = user; + this.password = password; + } + + public void run() throws SQLException { + try (Connection con = DriverManager.getConnection(url, user, password)) { + Statement st = con.createStatement(); + st.executeUpdate(SQL); + } } - } } diff --git a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java index c2779ce0d..63ab60324 100644 --- a/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java +++ b/edc-tests/cucumber/src/test/java/org/eclipse/tractusx/edc/tests/util/S3Client.java @@ -19,16 +19,9 @@ package org.eclipse.tractusx.edc.tests.util; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.edc.tests.Environment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.core.ResponseBytes; @@ -45,80 +38,89 @@ import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Object; -@Slf4j +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + + public class S3Client { + private static final Logger log = LoggerFactory.getLogger(S3Client.class); + private final software.amazon.awssdk.services.s3.S3Client s3; + + public S3Client(Environment environment) { + + s3 = + software.amazon.awssdk.services.s3.S3Client.builder() + .region(Region.US_EAST_1) + .forcePathStyle(true) + .endpointOverride(URI.create(environment.getAwsEndpointOverride())) + .credentialsProvider( + StaticCredentialsProvider.create( + AwsBasicCredentials.create( + environment.getAwsAccessKey(), environment.getAwsSecretAccessKey()))) + .build(); + } + + public void createBucket(String bucketName) { + try { + s3.createBucket(CreateBucketRequest.builder().bucket(bucketName).build()); + } catch (BucketAlreadyOwnedByYouException e) { + log.info("'{}' bucket already owned - skipped bucket creation", bucketName); + } + } + + public File uploadFile(String bucketName, String fileName) throws IOException { + File tempFile = File.createTempFile(fileName, null); + Files.write( + tempFile.toPath(), "Will fail if the file has no content".getBytes(StandardCharsets.UTF_8)); + + s3.putObject( + PutObjectRequest.builder().bucket(bucketName).key(fileName).build(), + RequestBody.fromFile(tempFile)); + + return tempFile; + } + + public List listBuckets() { + return s3.listBuckets().buckets().stream().map(Bucket::name).collect(Collectors.toList()); + } + + public Set listBucketContent(String bucketName) { + return s3 + .listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) + .contents() + .stream() + .map(S3Object::key) + .collect(Collectors.toSet()); + } + + public File downloadFile(String bucketName, String fileName) throws IOException { + ResponseBytes objectAsBytes = + s3.getObjectAsBytes(GetObjectRequest.builder().bucket(bucketName).key(fileName).build()); + + return Files.write(File.createTempFile(fileName, null).toPath(), objectAsBytes.asByteArray()) + .toFile(); + } + + public void deleteAllBuckets() { + List buckets = s3.listBuckets().buckets(); + buckets.forEach(this::clearBucket); + buckets.forEach( + bucket -> s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucket.name()).build())); + } - private final software.amazon.awssdk.services.s3.S3Client s3; - - public S3Client(Environment environment) { - - s3 = - software.amazon.awssdk.services.s3.S3Client.builder() - .region(Region.US_EAST_1) - .forcePathStyle(true) - .endpointOverride(URI.create(environment.getAwsEndpointOverride())) - .credentialsProvider( - StaticCredentialsProvider.create( - AwsBasicCredentials.create( - environment.getAwsAccessKey(), environment.getAwsSecretAccessKey()))) - .build(); - } - - public void createBucket(String bucketName) { - try { - s3.createBucket(CreateBucketRequest.builder().bucket(bucketName).build()); - } catch (BucketAlreadyOwnedByYouException e) { - log.info("'{}' bucket already owned - skipped bucket creation", bucketName); + private void clearBucket(Bucket bucket) { + String bucketName = bucket.name(); + s3.listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) + .contents() + .forEach( + s3Object -> + s3.deleteObject( + DeleteObjectRequest.builder().bucket(bucketName).key(s3Object.key()).build())); } - } - - public File uploadFile(String bucketName, String fileName) throws IOException { - File tempFile = File.createTempFile(fileName, null); - Files.write( - tempFile.toPath(), "Will fail if the file has no content".getBytes(StandardCharsets.UTF_8)); - - s3.putObject( - PutObjectRequest.builder().bucket(bucketName).key(fileName).build(), - RequestBody.fromFile(tempFile)); - - return tempFile; - } - - public List listBuckets() { - return s3.listBuckets().buckets().stream().map(Bucket::name).collect(Collectors.toList()); - } - - public Set listBucketContent(String bucketName) { - return s3 - .listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) - .contents() - .stream() - .map(S3Object::key) - .collect(Collectors.toSet()); - } - - public File downloadFile(String bucketName, String fileName) throws IOException { - ResponseBytes objectAsBytes = - s3.getObjectAsBytes(GetObjectRequest.builder().bucket(bucketName).key(fileName).build()); - - return Files.write(File.createTempFile(fileName, null).toPath(), objectAsBytes.asByteArray()) - .toFile(); - } - - public void deleteAllBuckets() { - List buckets = s3.listBuckets().buckets(); - buckets.forEach(this::clearBucket); - buckets.forEach( - bucket -> s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucket.name()).build())); - } - - private void clearBucket(Bucket bucket) { - String bucketName = bucket.name(); - s3.listObjects(ListObjectsRequest.builder().bucket(bucketName).build()) - .contents() - .forEach( - s3Object -> - s3.deleteObject( - DeleteObjectRequest.builder().bucket(bucketName).key(s3Object.key()).build())); - } } diff --git a/edc-tests/cucumber/src/test/resources/org/eclipse/tractusx/edc/tests/features/HttpProxyDataTransfer.feature b/edc-tests/cucumber/src/test/resources/org/eclipse/tractusx/edc/tests/features/HttpProxyDataTransfer.feature index d318ad745..b04970ec7 100644 --- a/edc-tests/cucumber/src/test/resources/org/eclipse/tractusx/edc/tests/features/HttpProxyDataTransfer.feature +++ b/edc-tests/cucumber/src/test/resources/org/eclipse/tractusx/edc/tests/features/HttpProxyDataTransfer.feature @@ -24,24 +24,6 @@ Feature: HttpProxy Data Transfer Given 'Plato' has an empty database Given 'Sokrates' has an empty database - Scenario: Connector transfers data via HttpProxy - Given 'Plato' has a http proxy assets - | id | description | baseUrl | - | asset-1 | http proxy transfer asset | http://localhost:8081/api/check/liveness | - And 'Plato' has the following policies - | id | action | - | policy-1 | USE | - And 'Plato' has the following contract definitions - | id | access policy | contract policy | asset | - | contract-definition-1 | policy-1 | policy-1 | asset-1 | - When 'Sokrates' negotiates the contract successfully with 'Plato' - | contract offer id | asset id | policy id | - | contract-definition-1 | asset-1 | policy-1 | - And 'Sokrates' initiates HttpProxy transfer from 'Plato' - | asset id | receiverHttpEndpoint | - | asset-1 | http://backend:8080 | - Then the backend application of 'Sokrates' has received data - Scenario: Connector transfers data via HttpProxy, data on provider side requires oauth2 authentication Given 'Plato' has a http proxy assets | id | description | baseUrl | oauth2 token url | oauth2 client id | oauth2 client secret | oauth2 scope | diff --git a/charts/edc-dataplane/.helmignore b/edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore similarity index 82% rename from charts/edc-dataplane/.helmignore rename to edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore index 148b31d6c..0e8a0eb36 100644 --- a/charts/edc-dataplane/.helmignore +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/.helmignore @@ -21,9 +21,3 @@ .idea/ *.tmproj .vscode/ - -README.md.gotmpl - -# Accept only values.yaml -values?*.yaml -values?*.yml diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml new file mode 100644 index 000000000..613b1f45c --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/Chart.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: v2 +name: ids-daps +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/README.md b/edc-tests/deployment/src/main/resources/helm/omejdn/README.md new file mode 100644 index 000000000..f85a94889 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/README.md @@ -0,0 +1,21 @@ +# Omejdn DAPS + +This chart deployes an [IDS Omejdn DAPS](https://github.com/Fraunhofer-AISEC/omejdn-server). + +Two Eclipse Dataspace Connectors need to be registered at the same DAPS instance, to be able to talk to each other. Each connector is registered in the DAPS by an unique client ID and a correpsonding client certificate. + +New connectors are configured in the omejdn _values.yaml_. + +In each Eclipse Dataspace Connector configure the following properties to use the DAPS. + +```properties + edc.oauth.client.id= + + edc.oauth.provider.jwks.url="http://:4567/.well-known/jwks.json" + edc.oauth.token.url="http://:4567/token" + + edc.oauth.private.key.alias= + edc.oauth.public.key.alias= + + edc.oauth.provider.audience=idsc:IDS_CONNECTORS_ALL +``` diff --git a/charts/edc-dataplane/templates/_helpers.tpl b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl similarity index 66% rename from charts/edc-dataplane/templates/_helpers.tpl rename to edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl index 3615298cd..95b115eee 100644 --- a/charts/edc-dataplane/templates/_helpers.tpl +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/_helpers.tpl @@ -1,7 +1,7 @@ {{/* Expand the name of the chart. */}} -{{- define "edc-dataplane.name" -}} +{{- define "omejdn.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} @@ -10,7 +10,7 @@ Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} -{{- define "edc-dataplane.fullname" -}} +{{- define "omejdn.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -26,17 +26,16 @@ If release name contains chart name it will be used as a full name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "edc-dataplane.chart" -}} +{{- define "omejdn.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} -{{- define "edc-dataplane.labels" -}} -helm.sh/chart: {{ include "edc-dataplane.chart" . }} -{{ include "edc-dataplane.selectorLabels" . }} -{{ include "edc-dataplane.customLabels" . }} +{{- define "omejdn.labels" -}} +helm.sh/chart: {{ include "omejdn.chart" . }} +{{ include "omejdn.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -46,26 +45,17 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{/* Selector labels */}} -{{- define "edc-dataplane.selectorLabels" -}} -app.kubernetes.io/name: {{ include "edc-dataplane.name" . }} +{{- define "omejdn.selectorLabels" -}} +app.kubernetes.io/name: {{ include "omejdn.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} -{{/* -Custom labels -*/}} -{{- define "edc-dataplane.customLabels" -}} -{{- with .Values.customLabels }} -{{ toYaml . }} -{{- end }} -{{- end }} - {{/* Create the name of the service account to use */}} -{{- define "edc-dataplane.serviceAccountName" -}} +{{- define "omejdn.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} -{{- default (include "edc-dataplane.fullname" .) .Values.serviceAccount.name }} +{{- default (include "omejdn.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml new file mode 100644 index 000000000..3d3e17c1f --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/configmap.yaml @@ -0,0 +1,73 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +data: + scope_mapping.yml: |- + --- + idsc:IDS_CONNECTOR_ATTRIBUTES_ALL: + - referringConnector + + omejdn.yml: |- + --- + host: http://ids-daps:4567/ + path_prefix: '' + bind_to: 0.0.0.0 + allow_origin: "*" + app_env: debug + openid: false + user_backend: + - yaml + user_backend_default: yaml + accept_audience: idsc:IDS_CONNECTORS_ALL + issuer: http://ids-daps:4567/ + environment: development + default_audience: + - idsc:IDS_CONNECTORS_ALL + access_token: + expiration: 3600 + algorithm: RS256 + id_token: + expiration: 3600 + algorithm: RS256 + + plugins.yml: |- + --- + plugins: + token_user_attributes: + + clients.yml: |- + --- + - client_id: data-plane-oauth2 + client_secret: supersecret + name: provision oauth2 + grant_types: + - client_credentials + token_endpoint_auth_method: client_secret_post + scope: openid +{{- range $i, $val := .Values.connectors }} + - client_id: {{ quote $val.id }} + name: {{ quote $val.name }} + token_endpoint_auth_method: private_key_jwt + grant_types: + - client_credentials + scope: + - idsc:IDS_CONNECTOR_ATTRIBUTES_ALL + attributes: + - key: idsc + value: IDS_CONNECTOR_ATTRIBUTES_ALL + - key: securityProfile + value: idsc:BASE_SECURITY_PROFILE + {{- range $key, $value := $val.attributes }} + - key: {{ $key }} + value: {{ $value }} + {{- end }} + redirect_uri: http://localhost:4200 +{{ end -}} + + +{{- range $i, $val := .Values.connectors }} + {{ $val.name }}: {{ quote $val.certificate | toString }} +{{ end -}} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml new file mode 100644 index 000000000..289476122 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/deployment.yaml @@ -0,0 +1,149 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "omejdn.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "omejdn.selectorLabels" . | nindent 8 }} + spec: + {{- if .Values.imagePullSecret.dockerconfigjson }} + imagePullSecrets: + - name: {{ include "omejdn.fullname" . }}-imagepullsecret + {{- else }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} + serviceAccountName: {{ include "omejdn.serviceAccountName" . }} + automountServiceAccountToken: {{ .Values.automountServiceAccountToken }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + initContainers: + - name: init-daps-pvc + image: alpine + command: + - "sh" + - "-c" + args: + - | + cp /opt/config/omejdn.yml /etc/daps/omejdn.yml + cp /opt/config/clients.yml /etc/daps/clients.yml + cp /opt/config/plugins.yml /etc/daps/plugins.yml + cp /opt/config/scope_mapping.yml /etc/daps/scope_mapping.yml + apk add --update openssl + openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /etc/keys/omejdn/omejdn.key \ + -subj "/C=DE/ST=Berlin/L=Berlin/O=TractusX-EDC-Test, Inc./OU=DE" + volumeMounts: + - mountPath: /etc/daps + name: config-dir + - mountPath: /etc/keys/omejdn + name: omejdn-key-dir + - mountPath: /opt/config/omejdn.yml + name: omejdn-config + subPath: omejdn.yml + - mountPath: /opt/config/scope_mapping.yml + name: scope-mapping + subPath: scope_mapping.yml + - mountPath: /opt/config/clients.yml + name: clients-config + subPath: clients.yml + - mountPath: /opt/config/plugins.yml + name: plugins-config + subPath: plugins.yml + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - mountPath: /opt/config/ + name: config-dir + - mountPath: /opt/keys/omejdn/omejdn.key + name: omejdn-key-dir + subPath: omejdn.key + - mountPath: /opt/keys/clients/ + name: client-certificates + ports: + - name: http + containerPort: 4567 + protocol: TCP + livenessProbe: + httpGet: + path: /jwks.json + port: http + readinessProbe: + httpGet: + path: /jwks.json + port: http + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + - name: OMEJDN_JWT_AUD_OVERRIDE + value: "idsc:IDS_CONNECTORS_ALL" + - name: OMEJDN_PLUGINS + value: "config/plugins.yml" + volumes: + - name: config-dir + emptyDir: {} + - name: omejdn-key-dir + emptyDir: {} + - name: omejdn-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: omejdn.yml + path: omejdn.yml + - name: scope-mapping + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: scope_mapping.yml + path: scope_mapping.yml + - name: clients-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: clients.yml + path: clients.yml + - name: plugins-config + configMap: + name: {{ include "omejdn.fullname" . }} + items: + - key: plugins.yml + path: plugins.yml + - name: client-certificates + configMap: + name: {{ include "omejdn.fullname" . }} + items: + {{- range $i, $val := .Values.connectors }} + - key: {{ $val.name }} + path: {{ $val.id }}.cert + {{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml new file mode 100644 index 000000000..ce2a70957 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "omejdn.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml new file mode 100644 index 000000000..d7c1d31d7 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/imagepullsecret.yaml @@ -0,0 +1,13 @@ +{{- if .Values.imagePullSecret.dockerconfigjson }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "edc-dataplane.fullname" . }}-imagepullsecret + namespace: {{ .Release.Namespace | default "default" | quote }} + labels: + {{- include "edc-dataplane.labels" . | nindent 4 }} +data: + .dockerconfigjson: {{ .Values.imagePullSecret.dockerconfigjson }} +type: kubernetes.io/dockerconfigjson +{{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml new file mode 100644 index 000000000..57dfe3921 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "omejdn.fullname" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "omejdn.selectorLabels" . | nindent 4 }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml new file mode 100644 index 000000000..17baf8239 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "omejdn.serviceAccountName" . }} + labels: + {{- include "omejdn.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml b/edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml new file mode 100644 index 000000000..ae82cd1d6 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/omejdn/values.yaml @@ -0,0 +1,91 @@ +--- +# Default values for omejdn. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- Specifies how many replicas of a deployed pod shall be created during the deployment +# Note: If horizontal pod autoscaling is enabled this setting has no effect +replicaCount: 1 + +image: + # -- Which omjedn container image to use + repository: ghcr.io/fraunhofer-aisec/omejdn-server + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "1.7.1" + +imagePullSecret: + # -- Image pull secret to create to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + # Note: This value needs to adhere to the [(base64 encoded) .dockerconfigjson format](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials). + # Furthermore, if 'imagePullSecret.dockerconfigjson' is defined, it takes precedence over 'imagePullSecrets'. + dockerconfigjson: "" + +# -- Overrides the charts name +nameOverride: "" + +# -- Overrides the releases full name +fullnameOverride: "" + +serviceAccount: + # -- Specifies whether a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) should be created per release + create: true + # -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) to add to the service account + annotations: {} + # -- The name of the service account to use. If not set and create is true, a name is generated using the release's fullname template + name: "" + +# -- Whether to [automount kubernetes API credentials](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) into the pod +automountServiceAccountToken: false + +# -- [Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) added to deployed [pods](https://kubernetes.io/docs/concepts/workloads/pods/) +podAnnotations: {} + +# The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment +podSecurityContext: {} + +# The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod +securityContext: {} + +service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) to expose the running application on a set of Pods as a network service. + port: 4567 + +# -- [Resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) applied to the deployed pod +resources: {} + +autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + +# -- [Node-Selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain the Pod to nodes with specific labels. +nodeSelector: {} + +# -- [Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) are applied to Pods to schedule onto nodes with matching taints. +tolerations: [] + +# -- [Affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) constrains which nodes the Pod can be scheduled on based on node labels. +affinity: {} + +# List of connector clients. Certificate and Client-ID must be configured in parallel. +#
+# Example Connector: +# - id: grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 +# name: my-connector +# attributes: +# issuerConnector: http://localhost:8080/ +# certificate: |- +# -----BEGIN CERTIFICATE----- +# foo +# -----END CERTIFICATE----- +connectors: [] diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore new file mode 100644 index 000000000..8681aba50 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.gitignore @@ -0,0 +1,4 @@ +# ignore downloaded helm depdencies +charts/ + +Chart.lock diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore new file mode 100644 index 000000000..8c60d7821 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/.helmignore @@ -0,0 +1,24 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +docs diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml new file mode 100644 index 000000000..6e7f24fe5 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml @@ -0,0 +1,54 @@ +--- +apiVersion: v2 +name: all-in-one +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" + +dependencies: + # IDS Dynamic Attribute Provisioning Service (IAM) + - name: ids-daps + version: 0.0.1 + repository: "file://../omejdn" + alias: idsdaps + condition: install.daps + + # HashiCorp Vault + - name: vault + alias: vault + version: 0.20.0 + repository: https://helm.releases.hashicorp.com + condition: install.vault + + # PostgreSQL + - name: postgresql + alias: postgresql + version: 12.1.6 + repository: https://charts.bitnami.com/bitnami + condition: install.postgresql + + # MinIo + - name: minio + alias: minio + repository: https://charts.min.io + version: 4.1.0 + condition: install.minio diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md new file mode 100644 index 000000000..e927d7bc5 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/README.md @@ -0,0 +1,54 @@ +# Supporting Infrastructure Deployment + +The Supporting Infrastructure Deployment creates a complete, independent and already configured EDC test environment. +During the automated business tests, these infrastructure components are deployed together with two connectors (Plato & Sokrates). + +This deployment could also be used as + +- reference setup for teams, that want to create their own connector +- standalone infrastructure to try things out + +This deployment should **never** be used + +- in **any** production or near production environments +- in **any** long living internet facing connector setups + +## Omejdn DAPS + +The Dynamic Attribute Provisioning Service (DAPS) is a component of the IDS Ecosystem. +The Fraunhofer Institute has created a DAPS reference implementation, the Omejdn +DAPS ([link](https://github.com/Fraunhofer-AISEC/omejdn-server)). This deplyoment configures and deployes a instance of +this reference implementation. + +Definition of DAPS from the IDS Reference architecture v3.0: + +> The Identity Provider acts as an agent for the International +> Data Spaces Association. It is responsible for issuing technical identities to parties that have been approved to become +> Participants in the International Data Spaces. The Identity +> Provider is instructed to issue identities based on approved +> roles (e.g., App Store or App Provider). Only if equipped with +> such an identity, an entity is allowed to participate in the International Data Spaces + +Also, please note, that the Omejdn DAPS is meant as research sandbox and should not be used in anq +productive environment. + +> **IMPORTANT:** Omejdn is meant to be a research sandbox in which we can (re)implement standard protocols and +> potentially extend and modify functionality under the hood to support research projects. Use at your own +> risk! ([source](https://github.com/Fraunhofer-AISEC/omejdn-server)) + +## HashiCorp Vault + +The Control- and Data Plane persist confidential in the vault and persist and communicate using only the secret +names. Hence, it is not possible to run a connector without an instance of a vault. + +## PostgreSQL + +This database is used to persist the state of the Control Plane. + +## Setup + +Simply execute the following comment in a shell: + +```shell +helm install infra edc-tests/deployment/src/main/resources/helm/test-infrastructure --update-dependencies +``` diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml new file mode 100644 index 000000000..feba29cc4 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml @@ -0,0 +1,185 @@ +--- + +########### +# Install # +########### +install: + daps: true + postgresql: true + vault: true + minio: false + + +######## +# DAPS # +######## +idsdaps: + fullnameOverride: "ids-daps" + connectors: + - id: E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65 + name: sokrates + attributes: + referringConnector: http://sokrates-controlplane/BPNSOKRATES + # Must be the same certificate that is stores in section 'sokrates-vault' + certificate: |- + -----BEGIN CERTIFICATE----- + MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ + TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK + DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD + DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw + 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa + llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV + grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 + PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 + ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT + MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH + LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd + iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E + 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 + S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r + uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB + UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + -----END CERTIFICATE----- + +############## +# PostgreSQL # +############## +postgresql: + fullnameOverride: "postgresql" + primary: + persistence: + enabled: false + readReplicas: + persistence: + enabled: false + auth: + database: "edc" + username: "user" + password: "password" + +######### +# MINIO # +######### +minio: + fullnameOverride: minio + replicas: 2 + drivesPerNode: 0 + serviceAccount: + create: false + persistence: + size: 128Mi + resources: + requests: + memory: 128Mi + service: + type: NodePort + control: + port: 9000 + users: + - accessKey: qwerty123 + secretKey: qwerty123 + policy: customBucketPolicy + buckets: + # in some cases the minio API acts strange if there exists no bucket at all + - name: dummybucket + policy: none + purge: true + policies: + - name: customBucketPolicy + statements: + - resources: + - 'arn:aws:s3:::*' + actions: + - "s3:PutObject" + - "s3:ListBucket" + - "s3:CreateBucket" + - "s3:GetObject" + - "s3:DeleteObject" + - "s3:DeleteBucket" + +######### +# VAULT # +######### +vault: + fullnameOverride: "vault" + injector: + enabled: false + server: + dev: + enabled: true + devRootToken: "root" + # Must be the same certificate that is configured in section 'ids-daps' + postStart: + - "sh" + - "-c" + - | + { + + sleep 5 + + /bin/vault kv put secret/sokrates/data-encryption-aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== + + cat << EOF | /bin/vault kv put secret/sokrates/daps/daps-key content=- + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM + wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ + FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 + 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ + ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE + sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc + RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z + d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm + hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm + cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh + FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F + MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e + uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb + ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 + z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 + h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ + vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB + 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 + hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 + dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 + Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 + IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT + 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr + 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 + u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B + AjWFbUiBCFOo+gpAFcQGrkOQHA== + -----END PRIVATE KEY----- + EOF + + cat << EOF | /bin/vault kv put secret/sokrates/daps/daps-crt content=- + -----BEGIN CERTIFICATE----- + MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ + TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK + DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD + DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw + 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa + llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV + grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 + PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 + ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT + MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH + LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd + iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E + 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 + S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r + uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB + UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + -----END CERTIFICATE----- + EOF + } diff --git a/edc-tests/e2e-tests/build.gradle.kts b/edc-tests/e2e-tests/build.gradle.kts index 6e2f7af27..d716d89c2 100644 --- a/edc-tests/e2e-tests/build.gradle.kts +++ b/edc-tests/e2e-tests/build.gradle.kts @@ -17,6 +17,7 @@ plugins { } dependencies { + testImplementation("com.squareup.okhttp3:mockwebserver:5.0.0-alpha.11") testImplementation(libs.restAssured) testImplementation(libs.postgres) testImplementation(libs.awaitility) @@ -28,10 +29,13 @@ dependencies { testImplementation(edc.core.api) testImplementation(edc.spi.catalog) testImplementation(edc.api.catalog) - testImplementation(testFixtures(edc.junit)) + testImplementation(edc.api.contractnegotiation) + testImplementation(edc.api.transferprocess) + testImplementation(edc.spi.dataplane.selector) + } // do not publish edcBuild { publish.set(false) -} \ No newline at end of file +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java index e007a824d..5bf2a2417 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/MultiRuntimeTest.java @@ -17,59 +17,89 @@ import org.junit.jupiter.api.extension.RegisterExtension; -import java.util.Map; +import java.util.HashMap; -import static java.lang.String.format; -import static org.eclipse.edc.junit.testfixtures.TestUtils.tempDirectory; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.IDS_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_CONNECTOR_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_CONNECTOR_PORT; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_DATAPLANE_CONTROL_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_IDS_API; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_IDS_API_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_MANAGEMENT_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_MANAGEMENT_PORT; -import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_ASSET_FILE; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_PUBLIC_API_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_CONNECTOR_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_CONNECTOR_PORT; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_DATAPLANE_CONTROL_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_IDS_API; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_IDS_API_PORT; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_MANAGEMENT_PATH; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_MANAGEMENT_PORT; +import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.SOKRATES_PUBLIC_API_PORT; public class MultiRuntimeTest { - public static final String SOKRATES_ASSET_PATH = format("%s/%s.txt", tempDirectory(), SOKRATES_ASSET_FILE); @RegisterExtension protected static Participant sokrates = new Participant( ":edc-tests:runtime", "SOKRATES", - Map.of( - "edc.ids.id", "urn:connector:sokrates", - "web.http.port", String.valueOf(SOKRATES_CONNECTOR_PORT), - "web.http.path", SOKRATES_CONNECTOR_PATH, - "edc.test.asset.path", SOKRATES_ASSET_PATH, - "web.http.management.port", String.valueOf(SOKRATES_MANAGEMENT_PORT), - "web.http.management.path", SOKRATES_MANAGEMENT_PATH, - "web.http.ids.port", String.valueOf(SOKRATES_IDS_API_PORT), - "web.http.ids.path", IDS_PATH, - "edc.api.auth.key", "testkey", - "ids.webhook.address", SOKRATES_IDS_API)); + new HashMap<>() { + { + put("edc.connector.name", "sokrates"); + put("edc.ids.id", "urn:connector:sokrates"); + put("web.http.port", String.valueOf(SOKRATES_CONNECTOR_PORT)); + put("web.http.path", SOKRATES_CONNECTOR_PATH); + put("web.http.management.port", String.valueOf(SOKRATES_MANAGEMENT_PORT)); + put("web.http.management.path", SOKRATES_MANAGEMENT_PATH); + put("web.http.ids.port", String.valueOf(SOKRATES_IDS_API_PORT)); + put("web.http.ids.path", IDS_PATH); + put("edc.api.auth.key", "testkey"); + put("ids.webhook.address", SOKRATES_IDS_API); + put("web.http.public.path", "/api/public"); + put("web.http.public.port", SOKRATES_PUBLIC_API_PORT); + + // embedded dataplane config + put("web.http.control.path", "/api/dataplane/control"); + put("web.http.control.port", SOKRATES_DATAPLANE_CONTROL_PORT); + put("edc.dataplane.token.validation.endpoint", "http://localhost:" + SOKRATES_DATAPLANE_CONTROL_PORT + "/api/dataplane/control/token"); + put("edc.dataplane.selector.httpplane.url", "http://localhost:" + SOKRATES_DATAPLANE_CONTROL_PORT + "/api/dataplane/control"); + put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); + put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); + put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + SOKRATES_PUBLIC_API_PORT + "/api/public\"}"); + put("edc.receiver.http.dynamic.endpoint", "http://localhost:" + SOKRATES_CONNECTOR_PORT + "/api/consumer/datareference"); + put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); + } + }); + @RegisterExtension protected static Participant plato = new Participant( ":edc-tests:runtime", "PLATO", - Map.of( - "edc.ids.id", "urn:connector:plato", - "web.http.default.port", String.valueOf(PLATO_CONNECTOR_PORT), - "web.http.default.path", PLATO_CONNECTOR_PATH, - "web.http.management.port", String.valueOf(PLATO_MANAGEMENT_PORT), - "web.http.management.path", PLATO_MANAGEMENT_PATH, - "web.http.ids.port", String.valueOf(PLATO_IDS_API_PORT), - "web.http.ids.path", IDS_PATH, - "edc.api.auth.key", "testkey", - "ids.webhook.address", PLATO_IDS_API)); - - + new HashMap<>() { + { + put("edc.connector.name", "plato"); + put("edc.ids.id", "urn:connector:plato"); + put("web.http.default.port", String.valueOf(PLATO_CONNECTOR_PORT)); + put("web.http.default.path", PLATO_CONNECTOR_PATH); + put("web.http.management.port", String.valueOf(PLATO_MANAGEMENT_PORT)); + put("web.http.management.path", PLATO_MANAGEMENT_PATH); + put("web.http.ids.port", String.valueOf(PLATO_IDS_API_PORT)); + put("web.http.ids.path", IDS_PATH); + put("edc.api.auth.key", "testkey"); + put("ids.webhook.address", PLATO_IDS_API); + put("web.http.public.port", PLATO_PUBLIC_API_PORT); + put("web.http.public.path", "/api/public"); + // embedded dataplane config + put("web.http.control.path", "/api/dataplane/control"); + put("web.http.control.port", PLATO_DATAPLANE_CONTROL_PORT); + put("edc.dataplane.token.validation.endpoint", "http://localhost:" + PLATO_DATAPLANE_CONTROL_PORT + "/api/dataplane/control/token"); + put("edc.dataplane.selector.httpplane.url", "http://localhost:" + PLATO_DATAPLANE_CONTROL_PORT + "/api/dataplane/control"); + put("edc.dataplane.selector.httpplane.sourcetypes", "HttpData"); + put("edc.dataplane.selector.httpplane.destinationtypes", "HttpProxy"); + put("edc.dataplane.selector.httpplane.properties", "{\"publicApiUrl\":\"http://localhost:" + PLATO_PUBLIC_API_PORT + "/api/public\"}"); + put("tractusx.businesspartnervalidation.log.agreement.validation", "true"); + } + }); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java index 7f17696ba..bd1546111 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java @@ -15,31 +15,47 @@ package org.eclipse.tractusx.edc.lifecycle; import io.restassured.specification.RequestSpecification; +import org.eclipse.edc.api.model.IdResponseDto; import org.eclipse.edc.api.query.QuerySpecDto; import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.connector.api.management.catalog.model.CatalogRequestDto; +import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractNegotiationDto; +import org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractOfferDescription; +import org.eclipse.edc.connector.api.management.contractnegotiation.model.NegotiationInitiateRequestDto; +import org.eclipse.edc.connector.api.management.transferprocess.model.TransferProcessDto; +import org.eclipse.edc.connector.api.management.transferprocess.model.TransferRequestDto; import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.junit.extensions.EdcRuntimeExtension; +import org.eclipse.edc.policy.model.PolicyRegistrationTypes; import org.eclipse.edc.spi.asset.AssetSelectorExpression; import org.eclipse.edc.spi.iam.IdentityService; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.system.injection.InjectionContainer; import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.HttpDataAddress; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.tractusx.edc.token.MockDapsService; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; import java.net.URI; +import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import static io.restassured.RestAssured.given; import static io.restassured.http.ContentType.JSON; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.lessThan; public class Participant extends EdcRuntimeExtension implements BeforeAllCallback, AfterAllCallback { @@ -48,8 +64,9 @@ public class Participant extends EdcRuntimeExtension implements BeforeAllCallbac private final String idsEndpoint; private final TypeManager typeManager = new TypeManager(); private final String idsId; - private DataWiper wiper; private final String bpn; + private final String backend; + private DataWiper wiper; public Participant(String moduleName, String runtimeName, Map properties) { super(moduleName, runtimeName, properties); @@ -58,23 +75,32 @@ public Participant(String moduleName, String runtimeName, Map pr this.apiKey = properties.get("edc.api.auth.key"); this.idsId = properties.get("edc.ids.id"); this.bpn = runtimeName + "-BPN"; + this.backend = properties.get("edc.receiver.http.dynamic.endpoint"); this.registerServiceMock(IdentityService.class, new MockDapsService(getBpn())); + + typeManager.registerTypes(PolicyRegistrationTypes.TYPES.toArray(Class[]::new)); + } @Override - public void beforeTestExecution(ExtensionContext extensionContext) throws Exception { + public void beforeTestExecution(ExtensionContext extensionContext) { //do nothing - we only want to start the runtime once wiper.clearPersistence(); } @Override - public void afterTestExecution(ExtensionContext context) throws Exception { + public void afterTestExecution(ExtensionContext context) { } @Override - protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { - super.bootExtensions(context, serviceExtensions); - wiper = new DataWiper(context); + public void beforeAll(ExtensionContext context) throws Exception { + //only run this once + super.beforeTestExecution(context); + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + super.afterTestExecution(context); } /** @@ -106,6 +132,31 @@ public void createAsset(String id, Map properties) { } + /** + * Creates an asset with the given ID and props using the participant's Data Management API + */ + public void createAsset(String id, Map asserProperties, HttpDataAddress address) { + asserProperties = new HashMap<>(asserProperties); + asserProperties.put("asset:prop:id", id); + asserProperties.put("asset:prop:description", "test description"); + + var asset = Map.of( + "asset", Map.of( + "id", id, + "properties", asserProperties + ), + "dataAddress", address + ); + + baseRequest() + .body(asset) + .when() + .post("/assets") + .then() + .statusCode(200) + .contentType(JSON); + } + /** * Creates a {@link org.eclipse.edc.connector.contract.spi.types.offer.ContractDefinition} using the participant's Data Management API */ @@ -168,6 +219,44 @@ public Catalog requestCatalog(Participant other, QuerySpecDto query) { return typeManager.readValue(body, Catalog.class); } + public String negotiateContract(Participant other, String assetId) { + var catalog = requestCatalog(other); + assertThat(catalog.getContractOffers()).withFailMessage("Catalog received from " + other.idsId + " was empty!").isNotEmpty(); + var response = baseRequest() + .when() + .body(NegotiationInitiateRequestDto.Builder.newInstance() + .connectorAddress(other.idsEndpoint + "/data") + .connectorId(getBpn()) + .offer(catalog.getContractOffers().stream().filter(o -> o.getAsset().getId().equals(assetId)) + .findFirst().map(co -> ContractOfferDescription.Builder.newInstance() + .assetId(assetId) + .offerId(co.getId()) + .policy(co.getPolicy()) + .validity(ChronoUnit.SECONDS.between(co.getContractStart(), co.getContractEnd().plus(Duration.ofMillis(500)))) // the plus 1 is required due to https://github.com/eclipse-edc/Connector/issues/2650 + .build()) + .orElseThrow((() -> new RuntimeException("A contract for assetId " + assetId + " could not be negotiated")))) + .build() + ) + .post("/contractnegotiations") + .then(); + + var body = response.extract().body().asString(); + assertThat(response.extract().statusCode()).withFailMessage(body).isBetween(200, 299); + + return typeManager.readValue(body, IdResponseDto.class).getId(); + } + + public ContractNegotiationDto getNegotiation(String negotiationId) { + var response = baseRequest() + .when() + .get("/contractnegotiations/" + negotiationId) + .then(); + + var body = response.extract().body().asString(); + assertThat(response.extract().statusCode()).withFailMessage(body).isBetween(200, 299); + return typeManager.readValue(body, ContractNegotiationDto.class); + } + /** * Returns this participant's IDS ID */ @@ -182,15 +271,71 @@ public String getBpn() { return bpn; } - @Override - public void beforeAll(ExtensionContext context) throws Exception { - //only run this once - super.beforeTestExecution(context); + public String requestTransfer(String contractId, String assetId, Participant other, DataAddress destination, String dataRequestId) { + var response = baseRequest() + .when() + .body(TransferRequestDto.Builder.newInstance() + .assetId(assetId) + .id(dataRequestId) + .connectorAddress(other.idsEndpoint + "/data") + .managedResources(false) + .contractId(contractId) + .connectorId(bpn) + .protocol("ids-multipart") + .dataDestination(destination) + .build()) + .post("/transferprocess") + .then(); + + var body = response.extract().body().asString(); + assertThat(response.extract().statusCode()).withFailMessage(body).isBetween(200, 299); + + return typeManager.readValue(body, IdResponseDto.class).getId(); + } + + public TransferProcessDto getTransferProcess(String transferProcessId) { + var json = baseRequest() + .when() + .get("/transferprocess/" + transferProcessId) + .then() + .statusCode(allOf(greaterThanOrEqualTo(200), lessThan(300))) + .extract().body().asString(); + + return typeManager.readValue(json, TransferProcessDto.class); + + } + + public EndpointDataReference getDataReference(String dataRequestId) { + var dataReference = new AtomicReference(); + + var result = given() + .when() + .get(backend + "/{id}", dataRequestId) + .then() + .statusCode(200) + .extract() + .body() + .as(EndpointDataReference.class); + dataReference.set(result); + + return dataReference.get(); + } + + public String pullData(EndpointDataReference edr, Map queryParams) { + var response = given() + .baseUri(edr.getEndpoint()) + .header(edr.getAuthKey(), edr.getAuthCode()) + .queryParams(queryParams) + .when() + .get(); + assertThat(response.statusCode()).isBetween(200, 300); + return response.body().asString(); } @Override - public void afterAll(ExtensionContext context) throws Exception { - super.afterTestExecution(context); + protected void bootExtensions(ServiceExtensionContext context, List> serviceExtensions) { + super.bootExtensions(context, serviceExtensions); + wiper = new DataWiper(context); } private RequestSpecification baseRequest() { diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java index 145ae476f..f865d39d9 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java @@ -14,39 +14,30 @@ package org.eclipse.tractusx.edc.lifecycle; -import java.util.concurrent.TimeUnit; - import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort; -public class TestRuntimeConfiguration { - - - public static final String IDS_PATH = "/api/v1/ids"; - - - public static final int PLATO_CONNECTOR_PORT = getFreePort(); - public static final int PLATO_MANAGEMENT_PORT = getFreePort(); - public static final String PLATO_CONNECTOR_PATH = "/api"; - public static final String PLATO_MANAGEMENT_PATH = "/api/v1/management"; - public static final String CONSUMER_CONNECTOR_MANAGEMENT_URL = "http://localhost:" + PLATO_MANAGEMENT_PORT + PLATO_MANAGEMENT_PATH; - public static final int PLATO_IDS_API_PORT = getFreePort(); - public static final String PLATO_IDS_API = "http://localhost:" + PLATO_IDS_API_PORT; - - public static final int SOKRATES_CONNECTOR_PORT = getFreePort(); - public static final int SOKRATES_MANAGEMENT_PORT = getFreePort(); - public static final String SOKRATES_CONNECTOR_PATH = "/api"; - public static final String SOKRATES_MANAGEMENT_PATH = "/api/v1/management"; - public static final int SOKRATES_IDS_API_PORT = getFreePort(); - public static final String SOKRATES_IDS_API = "http://localhost:" + SOKRATES_IDS_API_PORT; +class TestRuntimeConfiguration { - public static final String PROVIDER_IDS_API_DATA = "http://localhost:" + SOKRATES_IDS_API_PORT + IDS_PATH + "/data"; - public static final String PROVIDER_ASSET_ID = "test-document"; - public static final long CONTRACT_VALIDITY = TimeUnit.HOURS.toSeconds(1); + static final String IDS_PATH = "/api/v1/ids"; + static final int PLATO_CONNECTOR_PORT = getFreePort(); + static final int PLATO_MANAGEMENT_PORT = getFreePort(); + static final String PLATO_CONNECTOR_PATH = "/api"; + static final String PLATO_MANAGEMENT_PATH = "/api/v1/management"; + static final int PLATO_IDS_API_PORT = getFreePort(); + static final String PLATO_IDS_API = "http://localhost:" + PLATO_IDS_API_PORT; - public static final String SOKRATES_ASSET_FILE = "text-document.txt"; + static final int SOKRATES_CONNECTOR_PORT = getFreePort(); + static final int SOKRATES_MANAGEMENT_PORT = getFreePort(); + static final String SOKRATES_CONNECTOR_PATH = "/api"; + static final String SOKRATES_MANAGEMENT_PATH = "/api/v1/management"; + static final int SOKRATES_IDS_API_PORT = getFreePort(); + static final String SOKRATES_IDS_API = "http://localhost:" + SOKRATES_IDS_API_PORT; - public static final String PROVIDER_CONNECTOR_MANAGEMENT_URL = "http://localhost:" + SOKRATES_MANAGEMENT_PORT + SOKRATES_MANAGEMENT_PATH; + static final String SOKRATES_PUBLIC_API_PORT = String.valueOf(getFreePort()); + static final String PLATO_PUBLIC_API_PORT = String.valueOf(getFreePort()); + static final String PLATO_DATAPLANE_CONTROL_PORT = String.valueOf(getFreePort()); + static final String SOKRATES_DATAPLANE_CONTROL_PORT = String.valueOf(getFreePort()); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderEdcController.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderEdcController.java new file mode 100644 index 000000000..8ea80e745 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderEdcController.java @@ -0,0 +1,2 @@ +package org.eclipse.tractusx.edc.lifecycle.provider;public class ProviderEdcController { +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderServicesExtension.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderServicesExtension.java new file mode 100644 index 000000000..526ff6872 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/provider/ProviderServicesExtension.java @@ -0,0 +1,2 @@ +package org.eclipse.tractusx.edc.lifecycle.provider;public class ProviderServicesExtension { +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/policy/PolicyHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/policy/PolicyHelperFunctions.java index b255aa078..56d92afbe 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/policy/PolicyHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/policy/PolicyHelperFunctions.java @@ -24,6 +24,7 @@ import org.eclipse.edc.policy.model.OrConstraint; import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.policy.model.PolicyType; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -53,4 +54,16 @@ public static PolicyDefinition businessPartnerNumberPolicy(String id, String... .build()) .build()).build()).build(); } + + public static PolicyDefinition noConstraintPolicy(String id) { + return PolicyDefinition.Builder.newInstance() + .id(id) + .policy(Policy.Builder.newInstance() + .permission(Permission.Builder.newInstance() + .action(Action.Builder.newInstance().type("USE").build()) + .build()) + .type(PolicyType.SET) + .build()) + .build(); + } } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java index b7812ae4a..ec8045f5b 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/CatalogTest.java @@ -16,14 +16,8 @@ import org.eclipse.edc.api.query.QuerySpecDto; -import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.junit.annotations.EndToEndTest; -import org.eclipse.edc.policy.model.Action; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.policy.model.PolicyType; import org.eclipse.tractusx.edc.lifecycle.MultiRuntimeTest; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,16 +26,11 @@ import static java.util.stream.IntStream.range; import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.tractusx.edc.policy.PolicyHelperFunctions.businessPartnerNumberPolicy; +import static org.eclipse.tractusx.edc.policy.PolicyHelperFunctions.noConstraintPolicy; @EndToEndTest public class CatalogTest extends MultiRuntimeTest { - - @BeforeAll - static void setup() { - - } - @Test void requestCatalog_fulfillsPolicy_shouldReturnOffer() { // arrange @@ -133,16 +122,6 @@ void requestCatalog_of1000Assets_shouldContainAll() { } - private PolicyDefinition noConstraintPolicy(String id) { - return PolicyDefinition.Builder.newInstance() - .id(id) - .policy(Policy.Builder.newInstance() - .permission(Permission.Builder.newInstance() - .action(Action.Builder.newInstance().type("USE").build()) - .build()) - .type(PolicyType.SET) - .build()) - .build(); - } + } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java new file mode 100644 index 000000000..1f93ae5e7 --- /dev/null +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/HttpConsumerPullWithProxyTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.tests; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.eclipse.edc.connector.api.management.transferprocess.model.TransferProcessDto; +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates; +import org.eclipse.edc.connector.transfer.spi.types.TransferProcessStates; +import org.eclipse.edc.junit.annotations.EndToEndTest; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.HttpDataAddress; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.edc.lifecycle.MultiRuntimeTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.time.Duration; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import static java.time.Duration.ofSeconds; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.awaitility.pollinterval.FibonacciPollInterval.fibonacci; +import static org.eclipse.edc.connector.transfer.dataplane.spi.TransferDataPlaneConstants.HTTP_PROXY; +import static org.eclipse.tractusx.edc.policy.PolicyHelperFunctions.businessPartnerNumberPolicy; + +@EndToEndTest +public class HttpConsumerPullWithProxyTest extends MultiRuntimeTest { + private static final Duration ASYNC_TIMEOUT = ofSeconds(45); + private static final Duration ASYNC_POLL_INTERVAL = ofSeconds(1); + private final long ONE_WEEK = 60 * 60 * 24 * 7; + MockWebServer server = new MockWebServer(); + + @Test + void transferData_privateBackend() throws IOException, InterruptedException { + var assetId = "api-asset-1"; + var url = server.url("/mock/api"); + server.start(); + + var authCodeHeaderName = "test-authkey"; + var authCode = "test-authcode"; + plato.createAsset(assetId, Map.of(), HttpDataAddress.Builder.newInstance() + .contentType("application/json") + .baseUrl(url.toString()) + .authKey(authCodeHeaderName) + .authCode(authCode) + .build()); + plato.createPolicy(businessPartnerNumberPolicy("policy-1", sokrates.getBpn())); + plato.createPolicy(businessPartnerNumberPolicy("policy-2", sokrates.getBpn())); + plato.createContractDefinition(assetId, "def-1", "policy-1", "policy-2", ONE_WEEK); + var negotiationId = sokrates.negotiateContract(plato, assetId); + + // forward declarations of our actual values + var transferProcessId = new AtomicReference(); + var dataRequestId = UUID.randomUUID().toString(); + var contractAgreementId = new AtomicReference(); + var edr = new AtomicReference(); + + + // wait for the successful contract negotiation + await().pollInterval(ASYNC_POLL_INTERVAL) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var negotiation = sokrates.getNegotiation(negotiationId); + assertThat(negotiation.getState()).isEqualTo(ContractNegotiationStates.CONFIRMED.toString()); + contractAgreementId.set(negotiation.getContractAgreementId()); + assertThat(contractAgreementId).isNotNull(); + transferProcessId.set(sokrates.requestTransfer(contractAgreementId.get(), assetId, plato, DataAddress.Builder.newInstance() + .type(HTTP_PROXY) + .build(), dataRequestId)); + assertThat(transferProcessId).isNotNull(); + }); + + // wait until transfer process completes + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + var tp = sokrates.getTransferProcess(transferProcessId.get()); + assertThat(tp).isNotNull() + .extracting(TransferProcessDto::getState).isEqualTo(TransferProcessStates.COMPLETED.toString()); + }); + + // wait until EDC is available on the consumer side + server.enqueue(new MockResponse().setBody("test response").setResponseCode(200)); + await().pollInterval(fibonacci()) + .atMost(ASYNC_TIMEOUT) + .untilAsserted(() -> { + edr.set(sokrates.getDataReference(dataRequestId)); + assertThat(edr).isNotNull(); + }); + + // pull data out of provider's backend service: + // Cons-DP -> Prov-DP -> Prov-backend + assertThat(sokrates.pullData(edr.get(), Map.of())).isEqualTo("test response"); + var rq = server.takeRequest(); + assertThat(rq.getHeader(authCodeHeaderName)).isEqualTo(authCode); + assertThat(rq.getHeader("Edc-Contract-Agreement-Id")).isEqualTo(contractAgreementId.get()); + assertThat(rq.getMethod()).isEqualToIgnoringCase("GET"); + } + + @AfterEach + void teardown() throws IOException { + server.shutdown(); + } +} diff --git a/edc-tests/runtime/build.gradle.kts b/edc-tests/runtime/build.gradle.kts index dc31011c0..0123162fb 100644 --- a/edc-tests/runtime/build.gradle.kts +++ b/edc-tests/runtime/build.gradle.kts @@ -15,18 +15,28 @@ plugins { `java-library` id("application") - id("com.github.johnrengelman.shadow") version "8.0.0" + id("com.github.johnrengelman.shadow") version "8.1.1" } dependencies { - runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) { + // use basic (all in-mem) control plane + implementation(project(":edc-controlplane:edc-controlplane-base")) { exclude("org.eclipse.edc", "oauth2-core") exclude("org.eclipse.edc", "oauth2-daps") exclude(module = "data-encryption") exclude(module = "control-plane-adapter") } + + // use basic (all in-mem) data plane + runtimeOnly(project(":edc-dataplane:edc-dataplane-base")) { + exclude("org.eclipse.edc", "api-observability") + } + + implementation(edc.core.controlplane) + // for the controller + implementation(libs.jakarta.rsApi) } application { @@ -41,4 +51,4 @@ tasks.withType { // do not publish edcBuild { publish.set(false) -} \ No newline at end of file +} diff --git a/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java b/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java new file mode 100644 index 000000000..5ca08a922 --- /dev/null +++ b/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerEdrHandlerController.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.lifecycle; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Path("/consumer") +public class ConsumerEdrHandlerController { + + private final Monitor monitor; + private Map dataReference; + + public ConsumerEdrHandlerController(Monitor monitor) { + this.monitor = monitor; + dataReference = new HashMap<>(); + } + + @Path("/datareference") + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + public void pushDataReference(EndpointDataReference edr) { + monitor.debug("Received new endpoint data reference with url " + edr.getEndpoint()); + dataReference.put(edr.getId(), edr); + } + + @Path("/datareference/{id}") + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public EndpointDataReference getDataReference(@PathParam("id") String id) { + return Optional.ofNullable(dataReference.get(id)).orElseGet(() -> + { + monitor.warning("No EndpointDataReference found with id " + id); + return null; + }); + } + +} diff --git a/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java b/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java new file mode 100644 index 000000000..f46ef3e4e --- /dev/null +++ b/edc-tests/runtime/src/main/java/org/eclipse/tractusx/edc/lifecycle/ConsumerServicesExtension.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.lifecycle; + +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.web.spi.WebService; + +public class ConsumerServicesExtension implements ServiceExtension { + @Inject + private WebService webService; + + @Override + public void initialize(ServiceExtensionContext context) { + webService.registerResource("default", new ConsumerEdrHandlerController(context.getMonitor())); + } +} diff --git a/edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..619665085 --- /dev/null +++ b/edc-tests/runtime/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,15 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# +# 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: +# Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation +# +# + +org.eclipse.tractusx.edc.lifecycle.ConsumerServicesExtension diff --git a/gradle.properties b/gradle.properties index 206345621..73befaa64 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ groupId=org.eclipse.tractusx.edc -version=0.3.2 +version=0.3.3 javaVersion=11 # configure the build: diff --git a/pr_etiquette.md b/pr_etiquette.md index ce9ec73f8..348ebf000 100644 --- a/pr_etiquette.md +++ b/pr_etiquette.md @@ -10,7 +10,7 @@ Submitting pull requests in EDC should be done while adhering to a couple of sim - No surprise PRs please. Before you submit a PR, open a discussion or an issue outlining your planned work and give people time to comment. It may even be advisable to contact committers using the `@mention` feature. Unsolicited PRs may get ignored or rejected. -- Create your working branch in your fork of TractusX-EDC, and create the PR against the upstream `develop` branch +- Create your working branch in your fork of TractusX-EDC, and create the PR against the upstream `main` branch - Create focused PRs: your work should be focused on one particular feature or bug. Do not create broad-scoped PRs that solve multiple issues as reviewers may reject those PR bombs outright. - Provide a clear description and motivation in the PR description in GitHub. This makes the reviewer's life much @@ -58,6 +58,13 @@ Submitting pull requests in EDC should be done while adhering to a couple of sim - Be civil and objective. No foul language, insulting or otherwise abusive language will be tolerated. The goal is to _encourage_ contributions. -## The technical committers +## The technical committers (as of April 05, 2023) -- TBD +Main committers for the TractusX-EDC project: + +- @paullatzelsperger +- @florianrusch-zf + +Alternatively, the following Tractus-X committers can also step in: + +- @SebastianBezold diff --git a/settings.gradle.kts b/settings.gradle.kts index e0fc39433..a4158ef8c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,7 @@ include(":edc-tests:cucumber") // modules for controlplane artifacts include(":edc-controlplane") include(":edc-controlplane:edc-controlplane-base") -include(":edc-controlplane:edc-controlplane-memory") +include(":edc-controlplane:edc-runtime-memory") include(":edc-controlplane:edc-controlplane-memory-hashicorp-vault") include(":edc-controlplane:edc-controlplane-postgresql") include(":edc-controlplane:edc-controlplane-postgresql-hashicorp-vault") @@ -52,13 +52,13 @@ dependencyResolutionManagement { versionCatalogs { create("libs") { from("org.eclipse.edc:edc-versions:0.0.1-20230220-SNAPSHOT") - library("testcontainers-junit", "org.testcontainers","junit-jupiter").version("1.17.6") - library("apache-sshd-core", "org.apache.sshd","sshd-core").version("2.9.2") - library("apache-sshd-sftp", "org.apache.sshd","sshd-sftp").version("2.9.2") + library("testcontainers-junit", "org.testcontainers", "junit-jupiter").version("1.17.6") + library("apache-sshd-core", "org.apache.sshd", "sshd-core").version("2.9.2") + library("apache-sshd-sftp", "org.apache.sshd", "sshd-sftp").version("2.9.2") } // create version catalog for all EDC modules create("edc") { - version("edc", "0.0.1-20230220-SNAPSHOT") + version("edc", "0.0.1-20230220.patch1") library("spi-catalog", "org.eclipse.edc", "catalog-spi").versionRef("edc") library("spi-auth", "org.eclipse.edc", "auth-spi").versionRef("edc") library("spi-transfer", "org.eclipse.edc", "transfer-spi").versionRef("edc") @@ -86,6 +86,9 @@ dependencyResolutionManagement { library("api-management", "org.eclipse.edc", "management-api").versionRef("edc") library("api-catalog", "org.eclipse.edc", "catalog-api").versionRef("edc") library("api-observability", "org.eclipse.edc", "api-observability").versionRef("edc") + library("api-contractnegotiation", "org.eclipse.edc", "contract-negotiation-api").versionRef("edc") + library("api-dataplane", "org.eclipse.edc", "data-plane-api").versionRef("edc") + library("api-transferprocess", "org.eclipse.edc", "transfer-process-api").versionRef("edc") library("ext-http", "org.eclipse.edc", "http").versionRef("edc") library("spi-ids", "org.eclipse.edc", "ids-spi").versionRef("edc") library("ids", "org.eclipse.edc", "ids").versionRef("edc") @@ -140,6 +143,8 @@ dependencyResolutionManagement { "transfer-pull-http-dynamic-receiver" ).versionRef("edc") + library("transfer.receiver", "org.eclipse.edc", "transfer-pull-http-receiver").versionRef("edc") + bundle( "connector", listOf("boot", "core-connector", "core-jersey", "core-controlplane", "api-observability") From 52a3ecdf578584cf1d9b0072a67e8a4e4d80eb9b Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Wed, 19 Apr 2023 18:39:50 +0200 Subject: [PATCH 093/132] release-fix: allow manual entry of Docker tag --- .github/actions/publish-docker-image/action.yml | 7 ++++--- .github/workflows/publish-docker.yaml | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/actions/publish-docker-image/action.yml b/.github/actions/publish-docker-image/action.yml index 2f8a8c522..a252bf108 100644 --- a/.github/actions/publish-docker-image/action.yml +++ b/.github/actions/publish-docker-image/action.yml @@ -38,6 +38,9 @@ inputs: docker_token: required: false description: "DockerHub Token. No push is done if omitted" + docker_tag: + required: false + description: 'additional docker tags' runs: using: "composite" steps: @@ -72,9 +75,7 @@ runs: images: | ${{ inputs.namespace }}/${{ inputs.imagename }} tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} + type=semver,pattern={{version}},value=${{ inputs.docker_tag }} type=semver,pattern={{major}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{raw}} diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index 24aaf2ff4..e441bd63d 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -28,6 +28,9 @@ on: description: 'The namespace (=repo) in DockerHub' required: false default: "tractusx" + docker_tag: + description: 'Explicitly specify the Docker tag. Note that SHA and latest are added automatically.' + required: false concurrency: # cancel only running jobs on pull requests @@ -53,6 +56,7 @@ jobs: - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/publish-docker-image with: + docker_tag: ${{ inputs.docker_tag }} rootDir: edc-controlplane/${{ matrix.name }} imagename: ${{ matrix.name }} namespace: ${{ inputs.namespace }} @@ -76,6 +80,7 @@ jobs: - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/publish-docker-image with: + docker_tag: ${{ inputs.docker_tag }} rootDir: edc-dataplane/${{ matrix.name }} imagename: ${{ matrix.name }} namespace: ${{ inputs.namespace }} From 42b8d03eb7fc585b99614b02a354bd1a4b38ecc1 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Thu, 20 Apr 2023 09:09:59 +0200 Subject: [PATCH 094/132] chore: Add 0.3.3 to, and fix markdown in CHANGELOG.md (#252) --- CHANGELOG.md | 317 +++++++++--------- .../templates/deployment-runtime.yaml | 2 +- 2 files changed, 164 insertions(+), 155 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dadff38f6..7ce0ca990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.3.3] - 2023-04-19 +### Fixed + +- Config values for the data plane part of the helm chart +- Contract Validity + +### Added + +- A log line whenever a policy evaluation of the BPN number was performed + ## [0.3.2] - 2023-03-30 ### Fixed -- Fixed mutually-exclusive config values for Azure KeyVault +- Fixed mutually-exclusive config values for Azure KeyVault ## [0.3.1] - 2023-03-27 @@ -21,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Support unauthenticated access to the ObservabilityAPI (#126) +- Support unauthenticated access to the ObservabilityAPI (#126) ### Fixed @@ -32,144 +41,144 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). ### Added -- Add contract id to data source http call (#732) -- Support also support releases in ci pipeline -- Introduce typed object for oauth2 provisioning -- Add documentation -- Add test case -- Add client to omejdn -- add hydra deployment -- Configure dynamically HTTP Receiver callback endpoints. (#685) -- cp-adapter : code review, rollbacke name change (#664) -- Feature/cp adapter task 355 356 357 (#621) -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Local TXDC Setup Documentation (#618) -- Feature: Sftp Provisioner and Client (#554) +- Add contract id to data source http call (#732) +- Support also support releases in ci pipeline +- Introduce typed object for oauth2 provisioning +- Add documentation +- Add test case +- Add client to omejdn +- add hydra deployment +- Configure dynamically HTTP Receiver callback endpoints. (#685) +- cp-adapter : code review, rollbacke name change (#664) +- Feature/cp adapter task 355 356 357 (#621) +- Add Validity Mapping in ContractDefinitionStepDefs class +- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps +- Add validity attribute in class ContractDefinition +- Add Validity Mapping in ContractDefinitionStepDefs class +- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps +- Add validity attribute in class ContractDefinition +- Local TXDC Setup Documentation (#618) +- Feature: Sftp Provisioner and Client (#554) ### Changed -- Support horizontal edc scaling in cp adapter extension (#678) -- Use upstream jackson version (#741) -- Replace provision-oauth2 with data-plane-http-oauth2 -- docs: Update sample documentation (#671) -- chore: Disable build ci pipeline if just docu was updated (#705) -- Increase trivy timeout -- Remove not useful anymore custom-jsonld extension (#683) -- update setup docu (#654) -- remove trailing slash (#652) -- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) -- Feature/set charts deprecated (#628) -- update setup docu (#627) -- Feature/update txdc deployment downward capabilities (#625) -- remove git submodule (#619) -- Feature/update postman (#624) -- update control plane docu (#623) -- update postgresql version in Chart.yaml supporting-infrastructure (#622) -- update link to edc logo in README.md (#612) -- update description of supporting infrastructure deployment (#616) +- Support horizontal edc scaling in cp adapter extension (#678) +- Use upstream jackson version (#741) +- Replace provision-oauth2 with data-plane-http-oauth2 +- docs: Update sample documentation (#671) +- chore: Disable build ci pipeline if just docu was updated (#705) +- Increase trivy timeout +- Remove not useful anymore custom-jsonld extension (#683) +- update setup docu (#654) +- remove trailing slash (#652) +- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) +- Feature/set charts deprecated (#628) +- update setup docu (#627) +- Feature/update txdc deployment downward capabilities (#625) +- remove git submodule (#619) +- Feature/update postman (#624) +- update control plane docu (#623) +- update postgresql version in Chart.yaml supporting-infrastructure (#622) +- update link to edc logo in README.md (#612) +- update description of supporting infrastructure deployment (#616) ### Fixed -- bugfix: Fix slow AES encryption (#746) -- Fix typo in tractusx-connector values.yaml comment -- Fix not working docu link in README.md -- Fix typo in control-plane adapter README +- bugfix: Fix slow AES encryption (#746) +- Fix typo in tractusx-connector values.yaml comment +- Fix not working docu link in README.md +- Fix typo in control-plane adapter README ### Dependency updates -- Bump EDC to 20220220 (#767) -- Bump alpine (#749) -- Bump alpine (#750) -- Bump alpine (#752) -- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#753) -- Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) -- Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) -- Bump s3 from 2.19.33 to 2.20.0 -- Bump s3 from 2.19.27 to 2.19.33 -- Bump jaxb-runtime from 4.0.1 to 4.0.2 -- Bump spotless-maven-plugin from 2.31.0 to 2.32.0 -- Bump postgresql from 42.5.1 to 42.5.3 -- Bump nimbus-jose-jwt from 9.30 to 9.30.1 -- Bump lombok from 1.18.24 to 1.18.26 -- Bump flyway-core from 9.12.0 to 9.14.1 -- Bump jackson-bom from 2.14.0-rc2 to 2.14.2 -- Bump cucumber.version from 7.11.0 to 7.11.1 -- Bump azure-sdk-bom from 1.2.8 to 1.2.9 -- Bump mockito-bom from 5.0.0 to 5.1.1 -- Bump edc version to 0.0.1-20230131-SNAPSHOT -- Bump s3 from 2.19.18 to 2.19.27 -- Bump docker/build-push-action from 3 to 4 -- Bump nimbus-jose-jwt from 9.29 to 9.30 -- Bump spotless-maven-plugin from 2.30.0 to 2.31.0 -- Bump nimbus-jose-jwt from 9.28 to 9.29 -- Bump mockito-bom from 4.11.0 to 5.0.0 -- Bump edc version to 0.0.1-20230125-SNAPSHOT -- Bump flyway-core from 9.11.0 to 9.12.0 -- Bump s3 from 2.19.15 to 2.19.18 (#684) -- Bump mikefarah/yq from 4.30.6 to 4.30.8 (#682) -- Bump spotless-maven-plugin from 2.29.0 to 2.30.0 -- Bump edc version to 0.0.1-20230115-SNAPSHOT -- Bump cucumber.version from 7.10.1 to 7.11.0 (#672) -- Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#669) -- Bump s3 from 2.19.11 to 2.19.15 (#668) -- Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) -- Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) -- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#659) -- Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) -- Bump alpine (#658) -- Bump alpine (#661) -- Bump alpine (#662) -- Bump azure/setup-kubectl from 3.1 to 3.2 (#655) -- Bump junit-bom from 5.9.1 to 5.9.2 (#657) -- Bump s3 from 2.19.2 to 2.19.11 (#648) -- Bump actions/checkout from 3.2.0 to 3.3.0 (#647) -- Bump flyway-core from 9.10.2 to 9.11.0 (#646) -- Bump spotless-maven-plugin from 2.28.0 to 2.29.0 (#641) -- Bump mockito-bom from 4.10.0 to 4.11.0 (#637) -- Bump flyway-core from 9.10.1 to 9.10.2 (#632) -- Bump s3 from 2.19.1 to 2.19.2 (#631) -- Bump s3 from 2.18.41 to 2.19.1 (#626) -- Bump mikefarah/yq from 4.30.5 to 4.30.6 (#613) -- Bump cucumber.version from 7.10.0 to 7.10.1 (#614) -- Bump s3 from 2.18.40 to 2.18.41 (#615) -- Bump azure/setup-helm from 3.4 to 3.5 (#596) -- Bump actions/checkout from 3.1.0 to 3.2.0 (#598) -- Bump mockito-bom from 4.9.0 to 4.10.0 (#607) -- Bump s3 from 2.18.39 to 2.18.40 (#609) -- Bump flyway-core from 9.10.0 to 9.10.1 (#610) -- Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) -- Bump s3 from 2.18.35 to 2.18.39 (#606) +- Bump EDC to 20220220 (#767) +- Bump alpine (#749) +- Bump alpine (#750) +- Bump alpine (#752) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#753) +- Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) +- Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) +- Bump s3 from 2.19.33 to 2.20.0 +- Bump s3 from 2.19.27 to 2.19.33 +- Bump jaxb-runtime from 4.0.1 to 4.0.2 +- Bump spotless-maven-plugin from 2.31.0 to 2.32.0 +- Bump postgresql from 42.5.1 to 42.5.3 +- Bump nimbus-jose-jwt from 9.30 to 9.30.1 +- Bump lombok from 1.18.24 to 1.18.26 +- Bump flyway-core from 9.12.0 to 9.14.1 +- Bump jackson-bom from 2.14.0-rc2 to 2.14.2 +- Bump cucumber.version from 7.11.0 to 7.11.1 +- Bump azure-sdk-bom from 1.2.8 to 1.2.9 +- Bump mockito-bom from 5.0.0 to 5.1.1 +- Bump edc version to 0.0.1-20230131-SNAPSHOT +- Bump s3 from 2.19.18 to 2.19.27 +- Bump docker/build-push-action from 3 to 4 +- Bump nimbus-jose-jwt from 9.29 to 9.30 +- Bump spotless-maven-plugin from 2.30.0 to 2.31.0 +- Bump nimbus-jose-jwt from 9.28 to 9.29 +- Bump mockito-bom from 4.11.0 to 5.0.0 +- Bump edc version to 0.0.1-20230125-SNAPSHOT +- Bump flyway-core from 9.11.0 to 9.12.0 +- Bump s3 from 2.19.15 to 2.19.18 (#684) +- Bump mikefarah/yq from 4.30.6 to 4.30.8 (#682) +- Bump spotless-maven-plugin from 2.29.0 to 2.30.0 +- Bump edc version to 0.0.1-20230115-SNAPSHOT +- Bump cucumber.version from 7.10.1 to 7.11.0 (#672) +- Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#669) +- Bump s3 from 2.19.11 to 2.19.15 (#668) +- Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) +- Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#659) +- Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) +- Bump alpine (#658) +- Bump alpine (#661) +- Bump alpine (#662) +- Bump azure/setup-kubectl from 3.1 to 3.2 (#655) +- Bump junit-bom from 5.9.1 to 5.9.2 (#657) +- Bump s3 from 2.19.2 to 2.19.11 (#648) +- Bump actions/checkout from 3.2.0 to 3.3.0 (#647) +- Bump flyway-core from 9.10.2 to 9.11.0 (#646) +- Bump spotless-maven-plugin from 2.28.0 to 2.29.0 (#641) +- Bump mockito-bom from 4.10.0 to 4.11.0 (#637) +- Bump flyway-core from 9.10.1 to 9.10.2 (#632) +- Bump s3 from 2.19.1 to 2.19.2 (#631) +- Bump s3 from 2.18.41 to 2.19.1 (#626) +- Bump mikefarah/yq from 4.30.5 to 4.30.6 (#613) +- Bump cucumber.version from 7.10.0 to 7.10.1 (#614) +- Bump s3 from 2.18.40 to 2.18.41 (#615) +- Bump azure/setup-helm from 3.4 to 3.5 (#596) +- Bump actions/checkout from 3.1.0 to 3.2.0 (#598) +- Bump mockito-bom from 4.9.0 to 4.10.0 (#607) +- Bump s3 from 2.18.39 to 2.18.40 (#609) +- Bump flyway-core from 9.10.0 to 9.10.1 (#610) +- Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) +- Bump s3 from 2.18.35 to 2.18.39 (#606) ## [0.1.6] - 2023-02-20 ### Fixed -- SQL leakage issue -- Catalog pagination +- SQL leakage issue +- Catalog pagination ## [0.1.5] - 2023-02-13 ### Fixed -- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug -- Data Encryption extension: fixed usage of a blocking algorithm +- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug +- Data Encryption extension: fixed usage of a blocking algorithm ## [0.1.2] - 2022-09-30 ### Added -- Introduced DEPENDENCIES file +- Introduced DEPENDENCIES file ### Changed -- Moved helm charts from `deployment/helm` to `charts` -- Replaced distroless image with alpine in all docker images -- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` +- Moved helm charts from `deployment/helm` to `charts` +- Replaced distroless image with alpine in all docker images +- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` ## [0.1.1] - 2022-09-04 @@ -178,17 +187,17 @@ connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). ### Added -- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) +- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) ### Changed -- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) -- Helm Charts: TLS secret name is now configurable +- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) +- Helm Charts: TLS secret name is now configurable ### Fixed -- Connectors with Azure Vault extension are now starting - again [link](https://github.com/eclipse-edc/Connector/issues/1892) +- Connectors with Azure Vault extension are now starting + again [link](https://github.com/eclipse-edc/Connector/issues/1892) ## [0.1.0] - 2022-08-19 @@ -197,74 +206,74 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Added -- Control-Plane - extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) - - run the EDC with multiple data planes at once -- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) - - add data plane instances to the control plane by configuration -- Data-Plane - extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) - - transfer from and to AWS S3 buckets -- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) - - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) +- Control-Plane + extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) + - run the EDC with multiple data planes at once +- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) + - add data plane instances to the control plane by configuration +- Data-Plane + extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) + - transfer from and to AWS S3 buckets +- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) + - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) ### Changed -- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) -- EDC has been updated to - version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - - implications to the behavior of the connector have been covered in - the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) +- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) +- EDC has been updated to + version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - + implications to the behavior of the connector have been covered in + the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) ### Fixed -- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving - offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) -- Deletion of Asset becomes impossible when Contract Negotiation - exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) -- Deletion of Policy becomes impossible when Contract Definition - exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving + offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) +- Deletion of Asset becomes impossible when Contract Negotiation + exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) +- Deletion of Policy becomes impossible when Contract Definition + exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) ## [0.0.6] - 2022-07-29 ### Fixed -- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath - issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) - library +- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath + issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) + library ## [0.0.5] - 2022-07-28 ### Added -- EDC Health Checks for HashiCorp Vault +- EDC Health Checks for HashiCorp Vault ### Changed -- BusinessPartnerNumber constraint supports List structure -- Helm: Confidential EDC settings can be set using k8s secrets -- HashiCorp Vault API path configurable +- BusinessPartnerNumber constraint supports List structure +- Helm: Confidential EDC settings can be set using k8s secrets +- HashiCorp Vault API path configurable ## [0.0.4] - 2022-06-27 ### Added -- HashiCorp Vault Extension -- Control Plane with HashiCorp Vault and PostgreSQL support +- HashiCorp Vault Extension +- Control Plane with HashiCorp Vault and PostgreSQL support ### Changed -- Release Workflow now publishes EDC Extensions as Maven Artifacts +- Release Workflow now publishes EDC Extensions as Maven Artifacts ### Fixed -- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 - contract offers max. +- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 + contract offers max. ### Removed -- CosmosDB Control Plane -- Control API Extension for all Control Planes +- CosmosDB Control Plane +- Control API Extension for all Control Planes ## [0.0.3] - 2022-05-23 @@ -276,7 +285,7 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). [0.3.3]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.2...0.3.3 -[0.3.2]: https://github.com/catenax-ng/tx-tractusx-edc/compare/0.3.1...0.3.2 +[0.3.2]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.1...0.3.2 [0.3.1]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.0...0.3.1 diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml index 3e7bd89e3..82d162ad8 100644 --- a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -63,7 +63,7 @@ spec: {{- if .Values.runtime.image.repository }} image: "{{ .Values.runtime.image.repository }}:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" {{- else }} - image: "ghcr.io/catenax-ng/tx-tractusx-edc/edc-runtime-memory:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" + image: "tractusx/edc-runtime-memory:{{ .Values.runtime.image.tag | default .Chart.AppVersion }}" {{- end }} imagePullPolicy: {{ .Values.runtime.image.pullPolicy }} From 761ee878d0099123dadd1952f04351f6f7842066 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Thu, 20 Apr 2023 14:28:28 +0200 Subject: [PATCH 095/132] docs: add decision record about conventional commits (#255) * chore: Add 0.3.3 to, and fix markdown in CHANGELOG.md (#252) * docs: add decision record about conventional commits --- .../2023-04-20_conventional_commits/README.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 docs/development/decision-records/2023-04-20_conventional_commits/README.md diff --git a/docs/development/decision-records/2023-04-20_conventional_commits/README.md b/docs/development/decision-records/2023-04-20_conventional_commits/README.md new file mode 100644 index 000000000..d58b55c61 --- /dev/null +++ b/docs/development/decision-records/2023-04-20_conventional_commits/README.md @@ -0,0 +1,43 @@ +# Using Conventional Commit messages + +## Decision + +From now on, TractusX-EDC will use only conventional commit messages. The specification can be +found [here](https://www.conventionalcommits.org/en/v1.0.0/#summary) + +## Rationale + +Conventional commits create a structured, explicit and unambiguous commit history, that is easy to read and to +interpret. Conventional commits are widely used in the world of open source development. +On top of that, there +is [extensive tooling](https://www.conventionalcommits.org/en/about/#tooling-for-conventional-commits) to support the +creation, interpretation and enforcement of conventional commits. + +## Approach + +As a first step, we enforce conventional commits as part of our CI pipeline. TractusX-EDC is using +Squash-Rebase-merging, and the PR title is used as commit message. We will not dictate how people structure their +commits during the _development_ phase of their PR, but we _will_ enforce, that PR titles (and thus: merge commit +messages) are in the conventional commit format. + +To do that, we can use a very simple regex check on the PR title: + +```yaml +- uses: deepakputhraya/action-pr-title@master + with: + regex: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(\w+((,|\/|\\)?\s?\w+)+\))?!?: [\S ]{1,80}[^\.]$' + allowed_prefixes: 'build,chore,ci,docs,feat,fix,perf,refactor,revert,style,test' + prefix_case_sensitive: true +``` + +That way, we can catch malformed PR titles early, which would result in malformed _merge commit messages_. In addition, +we can +use any of the tools linked above to ensure commit messages, e.g. when merge commits are altered manually, etc. + +## Future outlook + +Once we have a structured commit history done in the conventional commit format, we can auto-generate changelogs, link +to (auto-generated) documentation, render visually appealing version information, etc. Essentially, we can use any +number of tooling on top of cc's. +One key aspect would be to get rid of the manual changelog, +see [this discussion](https://github.com/eclipse-tractusx/tractusx-edc/discussions/253). From 524914b0382e69459da5d7c8115d5a62d21daee7 Mon Sep 17 00:00:00 2001 From: "Florian Rusch (ZF Friedrichshafen AG)" Date: Thu, 20 Apr 2023 17:12:19 +0200 Subject: [PATCH 096/132] fix: README.md points to wrong helm chart (#261) * Fix wrong helm install command * Update README.md --- charts/tractusx-connector-memory/README.md | 2 +- charts/tractusx-connector-memory/README.md.gotmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md index 798342502..872827664 100644 --- a/charts/tractusx-connector-memory/README.md +++ b/charts/tractusx-connector-memory/README.md @@ -10,7 +10,7 @@ A Helm chart for Tractus-X Eclipse Data Space Connector based on memory ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 +helm install my-release tractusx-edc/tractusx-connector-memory --version 0.3.3 ``` ## Source Code diff --git a/charts/tractusx-connector-memory/README.md.gotmpl b/charts/tractusx-connector-memory/README.md.gotmpl index b1671f5a2..44500d3d1 100644 --- a/charts/tractusx-connector-memory/README.md.gotmpl +++ b/charts/tractusx-connector-memory/README.md.gotmpl @@ -12,7 +12,7 @@ ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector --version {{ .Version }} +helm install my-release tractusx-edc/tractusx-connector-memory --version {{ .Version }} ``` {{ template "chart.maintainersSection" . }} From 69e84e9fc5fef15f29f963fd5ea1efc1b32a5de4 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Fri, 21 Apr 2023 08:54:52 +0200 Subject: [PATCH 097/132] feature: add explicit docker image creation during release process (#251) * chore: Add 0.3.3 to, and fix markdown in CHANGELOG.md (#252) * feat(release): add explicit docker build job to release * simplify matrix --- .github/workflows/build.yaml | 45 +++++----------- .github/workflows/publish-docker.yaml | 44 ++++------------ .github/workflows/publish-new-release.yml | 29 ++++++++++ .github/workflows/veracode.yaml | 64 +++++------------------ 4 files changed, 65 insertions(+), 117 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0713b0857..fd3fb7a93 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -81,8 +81,8 @@ jobs: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - build-controlplane: - name: "Create Docker Images for the ControlPlane" + build-docker-images: + name: "Create Docker Images" runs-on: ubuntu-latest needs: [ secret-presence ] if: | @@ -90,42 +90,22 @@ jobs: strategy: fail-fast: false matrix: - name: - - edc-runtime-memory - - edc-controlplane-memory-hashicorp-vault - - edc-controlplane-postgresql - - edc-controlplane-postgresql-hashicorp-vault + variant: [ { dir: edc-controlplane, img: edc-runtime-memory }, + { dir: edc-controlplane, img: edc-controlplane-memory-hashicorp-vault }, + { dir: edc-controlplane, img: edc-controlplane-postgresql-hashicorp-vault }, + { dir: edc-controlplane, img: edc-controlplane-postgresql }, + { dir: edc-dataplane, img: edc-dataplane-azure-vault }, + { dir: edc-dataplane, img: edc-dataplane-hashicorp-vault } ] permissions: contents: write steps: - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/publish-docker-image + name: Publish ${{ matrix.variant.img }} with: - rootDir: edc-controlplane/${{ matrix.name }} - imagename: ${{ matrix.name }} - docker_user: ${{ secrets.DOCKER_HUB_USER }} - docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} - - build-dataplane: - name: "Create Docker Images for the DataPlane" - runs-on: ubuntu-latest - needs: [ secret-presence ] - if: | - needs.secret-presence.outputs.DOCKER_HUB_TOKEN - strategy: - fail-fast: false - matrix: - name: - - edc-dataplane-azure-vault - - edc-dataplane-hashicorp-vault - permissions: - contents: write - steps: - - uses: actions/checkout@v3.3.0 - - uses: ./.github/actions/publish-docker-image - with: - rootDir: edc-dataplane/${{ matrix.name }} - imagename: ${{ matrix.name }} + docker_tag: ${{ needs.release-version.outputs.RELEASE_VERSION }} + rootDir: ${{ matrix.variant.dir }}/${{ matrix.variant.img }} + imagename: ${{ matrix.variant.img }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} @@ -159,4 +139,3 @@ jobs: REPO: ${{ github.repository }} GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index e441bd63d..bbe7a5d10 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -38,51 +38,29 @@ concurrency: cancel-in-progress: true jobs: - create-docker-image-controlplane: + create-docker-image: name: "Create Docker Images for the ControlPlane" runs-on: ubuntu-latest strategy: fail-fast: false matrix: - name: - - edc-runtime-memory - - edc-controlplane-memory-hashicorp-vault - - edc-controlplane-postgresql - - edc-controlplane-postgresql-hashicorp-vault + variant: [ { dir: edc-controlplane, img: edc-runtime-memory }, + { dir: edc-controlplane, img: edc-controlplane-memory-hashicorp-vault }, + { dir: edc-controlplane, img: edc-controlplane-postgresql-hashicorp-vault }, + { dir: edc-controlplane, img: edc-controlplane-postgresql }, + { dir: edc-dataplane, img: edc-dataplane-azure-vault }, + { dir: edc-dataplane, img: edc-dataplane-hashicorp-vault } ] permissions: contents: write packages: write steps: - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/publish-docker-image + name: Publish ${{ matrix.variant.img }} with: - docker_tag: ${{ inputs.docker_tag }} - rootDir: edc-controlplane/${{ matrix.name }} - imagename: ${{ matrix.name }} + docker_tag: ${{ needs.release-version.outputs.RELEASE_VERSION }} + rootDir: ${{ matrix.variant.dir }}/${{ matrix.variant.img }} + imagename: ${{ matrix.variant.img }} namespace: ${{ inputs.namespace }} docker_user: ${{ secrets.DOCKER_HUB_USER }} docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} - - - create-docker-image-dataplane: - name: "Create Docker Images for the DataPlane" - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - name: - - edc-dataplane-azure-vault - - edc-dataplane-hashicorp-vault - permissions: - contents: write - packages: write - steps: - - uses: actions/checkout@v3.3.0 - - uses: ./.github/actions/publish-docker-image - with: - docker_tag: ${{ inputs.docker_tag }} - rootDir: edc-dataplane/${{ matrix.name }} - imagename: ${{ matrix.name }} - namespace: ${{ inputs.namespace }} - docker_user: ${{ secrets.DOCKER_HUB_USER }} - docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index fbd0780ca..0da6f5da5 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -73,6 +73,35 @@ jobs: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + docker-release: + name: Publish Docker images + runs-on: ubuntu-latest + needs: [ release-version ] + permissions: + contents: write + if: github.event.pull_request.merged == true && needs.release-version.outputs.RELEASE_VERSION + + strategy: + fail-fast: false + matrix: + variant: [{dir: edc-controlplane, img: edc-runtime-memory}, + {dir: edc-controlplane, img: edc-controlplane-memory-hashicorp-vault}, + {dir: edc-controlplane, img: edc-controlplane-postgresql-hashicorp-vault}, + {dir: edc-controlplane, img: edc-controlplane-postgresql}, + {dir: edc-dataplane, img: edc-dataplane-azure-vault}, + {dir: edc-dataplane, img: edc-dataplane-hashicorp-vault}] + + steps: + - uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/publish-docker-image + name: Publish ${{ matrix.variant.img }} + with: + docker_tag: ${{ needs.release-version.outputs.RELEASE_VERSION }} + rootDir: ${{ matrix.variant.dir }}/${{ matrix.variant.img }} + imagename: ${{ matrix.variant.img }} + docker_user: ${{ secrets.DOCKER_HUB_USER }} + docker_token: ${{ secrets.DOCKER_HUB_TOKEN }} + # Release: Helm Charts helm-release: name: Publish new helm release diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index 486c53096..184929db1 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -30,7 +30,7 @@ jobs: - name: Verify proper formatting run: ./gradlew spotlessCheck - build-controlplane: + build: runs-on: ubuntu-latest needs: [ secret-presence, verify-formatting ] permissions: @@ -38,73 +38,35 @@ jobs: strategy: fail-fast: false matrix: - name: - - edc-runtime-memory - - edc-controlplane-memory-hashicorp-vault - - edc-controlplane-postgresql - - edc-controlplane-postgresql-hashicorp-vault + variant: [ { dir: edc-controlplane, name: edc-runtime-memory }, + { dir: edc-controlplane, name: edc-controlplane-memory-hashicorp-vault }, + { dir: edc-controlplane, name: edc-controlplane-postgresql-hashicorp-vault }, + { dir: edc-controlplane, name: edc-controlplane-postgresql }, + { dir: edc-dataplane, name: edc-dataplane-azure-vault }, + { dir: edc-dataplane, name: edc-dataplane-hashicorp-vault } ] steps: # Set-Up - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/setup-java # Build - - name: Build Controlplane + - name: Build ${{ matrix.variant.name }} run: |- - ./gradlew -p edc-controlplane/${{ matrix.name }} shadowJar + ./gradlew -p ${{ matrix.variant.dir }}/${{ matrix.variant.name }} shadowJar env: GITHUB_PACKAGE_USERNAME: ${{ github.actor }} GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Tar gzip files for veracode upload run: |- - tar -czvf edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.tar.gz edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar + tar -czvf ${{ matrix.variant.dir }}/${{ matrix.variant.name }} /build/libs/${{ matrix.variant.name }}.tar.gz ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.jar - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY continue-on-error: true with: - appname: product-edc/${{ matrix.name }} + appname: product-edc/${{ matrix.variant.name }} createprofile: true - version: ${{ matrix.name }}-${{ github.sha }} - filepath: edc-controlplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.tar.gz + version: ${{ matrix.variant.name }}-${{ github.sha }} + filepath: ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.tar.gz vid: ${{ secrets.ORG_VERACODE_API_ID }} vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - - build-dataplane: - runs-on: ubuntu-latest - needs: [ secret-presence, verify-formatting ] - permissions: - contents: read - strategy: - fail-fast: false - matrix: - name: - - edc-dataplane-azure-vault - - edc-dataplane-hashicorp-vault - steps: - # Set-Up - - uses: actions/checkout@v3.3.0 - - uses: ./.github/actions/setup-java - # Build - - name: Build Dataplane - run: |- - ./gradlew -p edc-dataplane/${{ matrix.name }} shadowJar - env: - GITHUB_PACKAGE_USERNAME: ${{ github.actor }} - GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - name: Tar gzip files for veracode upload - run: |- - tar -czvf edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.tar.gz edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.jar - - name: Veracode Upload And Scan - uses: veracode/veracode-uploadandscan-action@v1.0 - if: | - needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY - continue-on-error: true - with: - appname: product-edc/${{ matrix.name }} - createprofile: true - version: ${{ matrix.name }}-${{ github.sha }} - filepath: edc-dataplane/${{ matrix.name }}/build/libs/${{ matrix.name }}.tar.gz - vid: ${{ secrets.ORG_VERACODE_API_ID }} - vkey: ${{ secrets.ORG_VERACODE_API_KEY }} - From cd799fd892e1649a0a3660c627667da1e61d7671 Mon Sep 17 00:00:00 2001 From: ndr_brt Date: Fri, 21 Apr 2023 10:06:25 +0200 Subject: [PATCH 098/132] build(deps): add constraints to avoid vulnerable transitive dependencies (#259) --- .../edc-controlplane-postgresql/build.gradle.kts | 5 +++++ edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts | 5 +++++ edc-extensions/control-plane-adapter/build.gradle.kts | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts index 5888c34c4..78b5e253f 100644 --- a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts @@ -11,6 +11,11 @@ dependencies { runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) runtimeOnly(project(":edc-extensions:postgresql-migration")) runtimeOnly(edc.azure.vault) + constraints { + implementation("net.minidev:json-smart:2.4.10") { + because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") + } + } runtimeOnly(edc.bundles.sqlstores) runtimeOnly(edc.transaction.local) runtimeOnly(edc.sql.pool) diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index 02d29b7db..020dc0512 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -8,6 +8,11 @@ plugins { dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) implementation(edc.azure.vault) + constraints { + implementation("net.minidev:json-smart:2.4.10") { + because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") + } + } implementation(edc.azure.identity) implementation("com.azure:azure-security-keyvault-secrets:4.6.0") } diff --git a/edc-extensions/control-plane-adapter/build.gradle.kts b/edc-extensions/control-plane-adapter/build.gradle.kts index fe34a0866..c205d5cb0 100644 --- a/edc-extensions/control-plane-adapter/build.gradle.kts +++ b/edc-extensions/control-plane-adapter/build.gradle.kts @@ -8,7 +8,14 @@ plugins { dependencies { implementation(edc.spi.core) implementation(edc.spi.policy) + implementation(edc.api.management) + constraints { + implementation("org.yaml:snakeyaml:2.0") { + because("version 1.33 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-1471.") + } + } + implementation(edc.spi.catalog) implementation(edc.spi.transactionspi) implementation(edc.spi.transaction.datasource) From 76f2a1567a8e28c90f2041ada7f0f54cb160d85d Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" <100704677+tuncaytunc-zf@users.noreply.github.com> Date: Fri, 21 Apr 2023 10:51:38 +0200 Subject: [PATCH 099/132] chore: Rename Veracode appname in CI job (#265) Co-authored-by: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> --- .github/workflows/veracode.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index 184929db1..2c97790a9 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -64,7 +64,7 @@ jobs: needs.secret-presence.outputs.ORG_VERACODE_API_ID && needs.secret-presence.outputs.ORG_VERACODE_API_KEY continue-on-error: true with: - appname: product-edc/${{ matrix.variant.name }} + appname: tractusx-edc/${{ matrix.variant.name }} createprofile: true version: ${{ matrix.variant.name }}-${{ github.sha }} filepath: ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.tar.gz From 0579cc6be030e9fea5732dbd06e1dbac1b424516 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" <100704677+tuncaytunc-zf@users.noreply.github.com> Date: Fri, 21 Apr 2023 11:29:47 +0200 Subject: [PATCH 100/132] fix: Typo in veracode action (#267) --- .github/workflows/veracode.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index 2c97790a9..b8900971c 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -57,7 +57,7 @@ jobs: GITHUB_PACKAGE_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - name: Tar gzip files for veracode upload run: |- - tar -czvf ${{ matrix.variant.dir }}/${{ matrix.variant.name }} /build/libs/${{ matrix.variant.name }}.tar.gz ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.jar + tar -czvf ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.tar.gz ${{ matrix.variant.dir }}/${{ matrix.variant.name }}/build/libs/${{ matrix.variant.name }}.jar - name: Veracode Upload And Scan uses: veracode/veracode-uploadandscan-action@v1.0 if: | From 04e632ed21f387be6dfdd9bff14e6f33087d3898 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" <100704677+tuncaytunc-zf@users.noreply.github.com> Date: Fri, 21 Apr 2023 12:06:38 +0200 Subject: [PATCH 101/132] Adapt Postman collection for 0.3.x (#232) --- docs/development/postman/collection.json | 258 ++++++++++-------- .../development/postman/images/screenshot.png | Bin 77083 -> 0 bytes 2 files changed, 145 insertions(+), 113 deletions(-) delete mode 100644 docs/development/postman/images/screenshot.png diff --git a/docs/development/postman/collection.json b/docs/development/postman/collection.json index 50d0c5ab7..26de5c7d2 100644 --- a/docs/development/postman/collection.json +++ b/docs/development/postman/collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "b61c0075-e360-45df-9756-c9bc432fe76a", + "_postman_id": "fcea09d2-13d9-49ce-8c44-d3cb3078eb82", "name": "EDC", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "6134257" @@ -12,12 +12,11 @@ "method": "GET", "header": [], "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/assets/{{ASSET_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets/{{ASSET_ID}}", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "assets", "{{ASSET_ID}}" ] @@ -31,12 +30,11 @@ "method": "GET", "header": [], "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/assets", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "assets" ] } @@ -71,12 +69,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/assets", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "assets" ] } @@ -89,12 +86,11 @@ "method": "DELETE", "header": [], "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/assets/{{ASSET_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/assets/{{ASSET_ID}}", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "assets", "{{ASSET_ID}}" ] @@ -120,12 +116,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions/{{POLICY_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions/{{POLICY_ID}}", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "policydefinitions", "{{POLICY_ID}}" ] @@ -151,12 +146,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "policydefinitions" ] } @@ -178,12 +172,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "policydefinitions" ] } @@ -205,12 +198,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "policydefinitions" ] } @@ -232,12 +224,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "policydefinitions" ] } @@ -259,12 +250,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/policydefinitions/{{POLICY_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/policydefinitions/{{POLICY_ID}}", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "policydefinitions", "{{POLICY_ID}}" ] @@ -290,12 +280,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/contractdefinitions/{{POLICY_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions/{{POLICY_ID}}", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "contractdefinitions", "{{POLICY_ID}}" ] @@ -321,12 +310,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/contractdefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "contractdefinitions" ] } @@ -348,12 +336,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/contractdefinitions", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "contractdefinitions" ] } @@ -375,12 +362,11 @@ } }, "url": { - "raw": "{{PROVIDER_DATAMGMT_URL}}/data/contractdefinitions/{{POLICY_ID}}", + "raw": "{{PROVIDER_MANAGEMENT_URL}}/contractdefinitions/{{POLICY_ID}}", "host": [ - "{{PROVIDER_DATAMGMT_URL}}" + "{{PROVIDER_MANAGEMENT_URL}}" ], "path": [ - "data", "contractdefinitions", "{{POLICY_ID}}" ] @@ -394,18 +380,17 @@ "method": "GET", "header": [], "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/catalog?providerUrl={{PROVIDER_IDS_URL}}/api/v1/ids/data&size=50", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/catalog?providerUrl={{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data&size=50", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", "catalog" ], "query": [ { "key": "providerUrl", - "value": "{{PROVIDER_IDS_URL}}/api/v1/ids/data" + "value": "{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data" }, { "key": "size", @@ -423,7 +408,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"providerUrl\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\",\r\n \"querySpec\": {\r\n \"offset\": 0,\r\n \"limit\": 100,\r\n \"filter\": \"\",\r\n \"range\": {\r\n \"from\": 0,\r\n \"to\": 100\r\n },\r\n \"sortOrder\": \"ASC\",\r\n \"sortField\": \"\"\r\n }\r\n}", + "raw": "{\r\n \"providerUrl\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\",\r\n \"querySpec\": {\r\n \"offset\": 0,\r\n \"limit\": 100,\r\n \"sort\": \"ASC\",\r\n \"sortField\": \"\"\r\n }\r\n}", "options": { "raw": { "language": "json" @@ -431,12 +416,11 @@ } }, "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/catalog/request", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/catalog/request", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", "catalog", "request" ] @@ -445,46 +429,48 @@ "response": [] }, { - "name": "Negotation", + "name": "Negotation (Public)", "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - }, { "listen": "test", "script": { "exec": [ - "" + "pm.test(\"Body matches string\", function () {", + " var jsonData = pm.response.json();", + " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", + "", + "});" ], "type": "text/javascript" } } ], "request": { - "method": "GET", + "method": "POST", "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{CONTRACT_DEFINITION_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/contractnegotiations/{{NEGOTIATION_ID}}", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", - "contractnegotiations", - "{{NEGOTIATION_ID}}" + "contractnegotiations" ] } }, "response": [] }, { - "name": "Negotation (Public)", + "name": "Negotation (Properties)", "event": [ { "listen": "test", @@ -505,7 +491,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{CONTRACT_DEFINITION_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ]\n }\n }\n}", + "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ],\n \"extensibleProperties\": {\n \"foo\": \"bar\"\n }\n }\n }\n}", "options": { "raw": { "language": "json" @@ -513,12 +499,11 @@ } }, "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/contractnegotiations", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", "contractnegotiations" ] } @@ -526,7 +511,7 @@ "response": [] }, { - "name": "Negotation (Properties)", + "name": "Negotation (BPN)", "event": [ { "listen": "test", @@ -547,7 +532,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": []\n }\n ],\n \"extensibleProperties\": {\n \"foo\": \"bar\"\n }\n }\n }\n}", + "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": [\n {\n \"edctype\": \"AtomicConstraint\",\n \"leftExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"BusinessPartnerNumber\"\n },\n \"rightExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"{{POLICY_BPN}}\"\n },\n \"operator\": \"EQ\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -555,12 +540,11 @@ } }, "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/contractnegotiations", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", "contractnegotiations" ] } @@ -568,16 +552,24 @@ "response": [] }, { - "name": "Negotation (BPN)", + "name": "Negotation (init AGREEMENT_ID)", "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, { "listen": "test", "script": { "exec": [ - "pm.test(\"Body matches string\", function () {", - " var jsonData = pm.response.json();", - " pm.collectionVariables.set(\"NEGOTIATION_ID\", jsonData.id);", - "", + "pm.test(\"Body matches string\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.collectionVariables.set(\"AGREEMENT_ID\", jsonData.contractAgreementId);\r", "});" ], "type": "text/javascript" @@ -585,25 +577,16 @@ } ], "request": { - "method": "POST", + "method": "GET", "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"connectorId\": \"foo\",\n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\",\n \"offer\": {\n \"offerId\": \"{{POLICY_ID}}:foo\",\n \"assetId\": \"{{ASSET_ID}}\",\n \"policy\": {\n \"prohibitions\": [],\n \"obligations\": [],\n \"permissions\": [\n {\n \"edctype\": \"dataspaceconnector:permission\",\n \"action\": {\n \"type\": \"USE\"\n },\n \"target\": \"{{ASSET_ID}}\",\n \"constraints\": [\n {\n \"edctype\": \"AtomicConstraint\",\n \"leftExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"BusinessPartnerNumber\"\n },\n \"rightExpression\": {\n \"edctype\": \"dataspaceconnector:literalexpression\",\n \"value\": \"{{POLICY_BPN}}\"\n },\n \"operator\": \"EQ\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/contractnegotiations", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/contractnegotiations/{{NEGOTIATION_ID}}", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", - "contractnegotiations" + "contractnegotiations", + "{{NEGOTIATION_ID}}" ] } }, @@ -639,7 +622,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{ \"id\": \"{{TRANSFER_ID}}\",\n \"connectorId\": \"foo\", \n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\", \n \"contractId\": \"{{AGREEMENT_ID}}\", \n \"assetId\": \"{{ASSET_ID}}\",\n \"managedResources\": \"false\", \n \"dataDestination\": { \"type\": \"HttpProxy\" }\n}", + "raw": "{ \"id\": \"{{TRANSFER_ID}}\",\n \"connectorId\": \"foo\", \n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\", \n \"contractId\": \"{{AGREEMENT_ID}}\", \n \"assetId\": \"{{ASSET_ID}}\",\n \"managedResources\": \"false\", \n \"dataDestination\": { \"type\": \"HttpProxy\" }\n}", "options": { "raw": { "language": "json" @@ -647,12 +630,11 @@ } }, "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/transferprocess", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocess", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", "transferprocess" ] } @@ -689,7 +671,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{ \"id\": \"{{TRANSFER_ID}}\",\n \"connectorId\": \"foo\", \n \"connectorAddress\": \"{{PROVIDER_IDS_URL}}/api/v1/ids/data\", \n \"contractId\": \"{{AGREEMENT_ID}}\", \n \"assetId\": \"{{ASSET_ID}}\",\n \"managedResources\": \"false\", \n \"dataDestination\": { \"type\": \"HttpProxy\" },\n \"properties\": {\n \"receiver.http.endpoint\": \"{{BACKEND_SERVICE}}\"\n }\n}", + "raw": "{ \"id\": \"{{TRANSFER_ID}}\",\n \"connectorId\": \"foo\", \n \"connectorAddress\": \"{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data\", \n \"contractId\": \"{{AGREEMENT_ID}}\", \n \"assetId\": \"{{ASSET_ID}}\",\n \"managedResources\": \"false\", \n \"dataDestination\": { \"type\": \"HttpProxy\" },\n \"properties\": {\n \"receiver.http.endpoint\": \"{{BACKEND_SERVICE}}\"\n }\n}", "options": { "raw": { "language": "json" @@ -697,12 +679,11 @@ } }, "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/transferprocess", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocess", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", "transferprocess" ] } @@ -735,18 +716,61 @@ "method": "GET", "header": [], "url": { - "raw": "{{CONSUMER_DATAMGMT_URL}}/data/transferprocess/{{TRANSFER_PROCESS_ID}}", + "raw": "{{CONSUMER_MANAGEMENT_URL}}/transferprocess/{{TRANSFER_PROCESS_ID}}", "host": [ - "{{CONSUMER_DATAMGMT_URL}}" + "{{CONSUMER_MANAGEMENT_URL}}" ], "path": [ - "data", "transferprocess", "{{TRANSFER_PROCESS_ID}}" ] } }, "response": [] + }, + { + "name": "CPA (getData)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Body matches string\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.collectionVariables.set(\"authCode\", jsonData.authCode);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{CONSUMER_MANAGEMENT_URL}}/adapter/asset/sync/{{ASSET_ID}}?providerUrl={{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data&contractAgreementReuse=false", + "host": [ + "{{CONSUMER_MANAGEMENT_URL}}" + ], + "path": [ + "adapter", + "asset", + "sync", + "{{ASSET_ID}}" + ], + "query": [ + { + "key": "providerUrl", + "value": "{{PROVIDER_PROTOCOL_URL}}/api/v1/ids/data" + }, + { + "key": "contractAgreementReuse", + "value": "false" + } + ] + } + }, + "response": [] } ], "auth": { @@ -786,16 +810,16 @@ ], "variable": [ { - "key": "CONSUMER_DATAMGMT_URL", - "value": "https://sokrates-txdc.int.demo.catena-x.net" + "key": "CONSUMER_MANAGEMENT_URL", + "value": "https://sokrates-txdc.int.demo.catena-x.net/management" }, { - "key": "PROVIDER_IDS_URL", + "key": "PROVIDER_PROTOCOL_URL", "value": "https://plato-txdc.int.demo.catena-x.net" }, { - "key": "PROVIDER_DATAMGMT_URL", - "value": "https://plato-txdc.int.demo.catena-x.net" + "key": "PROVIDER_MANAGEMENT_URL", + "value": "https://plato-txdc.int.demo.catena-x.net/management" }, { "key": "ASSET_ID", @@ -847,6 +871,14 @@ "key": "BACKEND_SERVICE", "value": "http://backend:8080", "type": "string" + }, + { + "key": "AGREEMENT-ID", + "value": "" + }, + { + "key": "authCode", + "value": "" } ] } \ No newline at end of file diff --git a/docs/development/postman/images/screenshot.png b/docs/development/postman/images/screenshot.png deleted file mode 100644 index 8a9d231c67352b39336f5362a5eb2ea12aa7b75e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77083 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%0DNtR|7lBA}M3~B_qYm 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 From 416531c6f97daad5f5c2bcd1b39a83e60521a9fe Mon Sep 17 00:00:00 2001 From: bcronin90 <90203222+bcronin90@users.noreply.github.com> Date: Fri, 21 Apr 2023 14:25:09 +0200 Subject: [PATCH 102/132] feat: Add/update documentation for connector kit (#138) Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) Co-authored-by: Stephan Bauer Co-authored-by: stephanbcbauer <84396022+stephanbcbauer@users.noreply.github.com> --- docs/development/Release.md | 6 + .../kit/adoption-view/images/domain-model.png | Bin 0 -> 142498 bytes .../adoption-view/images/edc_architecture.png | Bin 0 -> 7768 bytes .../kit/adoption-view/images/edc_overview.png | Bin 0 -> 430994 bytes docs/kit/adoption-view/page_adoption-view.md | 48 ++++++ docs/kit/adoption-view/page_domain_model.md | 64 ++++++++ .../page00_development_view.md | 24 +++ .../page01_eclipse_foundation.md | 35 +++++ .../page02_repository_structure.md | 26 ++++ .../page03_project_structure.md | 21 +++ .../operation-view/page00_operation_view.md | 24 +++ .../page02_technical_prerequisites.md | 43 ++++++ .../page03_local_setup_controlplane.md | 141 ++++++++++++++++++ .../page04_local_setup_dataplane.md | 98 ++++++++++++ .../operation-view/page06_kubernetes_setup.md | 22 +++ docs/kit/operation-view/page08_api.md | 64 ++++++++ docs/kit/operation-view/page09_upgrading.md | 20 +++ docs/kit/operation-view/page10_extensions.md | 44 ++++++ 18 files changed, 680 insertions(+) create mode 100644 docs/kit/adoption-view/images/domain-model.png create mode 100644 docs/kit/adoption-view/images/edc_architecture.png create mode 100644 docs/kit/adoption-view/images/edc_overview.png create mode 100644 docs/kit/adoption-view/page_adoption-view.md create mode 100644 docs/kit/adoption-view/page_domain_model.md create mode 100644 docs/kit/development-view/page00_development_view.md create mode 100644 docs/kit/development-view/page01_eclipse_foundation.md create mode 100644 docs/kit/development-view/page02_repository_structure.md create mode 100644 docs/kit/development-view/page03_project_structure.md create mode 100644 docs/kit/operation-view/page00_operation_view.md create mode 100644 docs/kit/operation-view/page02_technical_prerequisites.md create mode 100644 docs/kit/operation-view/page03_local_setup_controlplane.md create mode 100644 docs/kit/operation-view/page04_local_setup_dataplane.md create mode 100644 docs/kit/operation-view/page06_kubernetes_setup.md create mode 100644 docs/kit/operation-view/page08_api.md create mode 100644 docs/kit/operation-view/page09_upgrading.md create mode 100644 docs/kit/operation-view/page10_extensions.md diff --git a/docs/development/Release.md b/docs/development/Release.md index 3992c0a1d..5f2fbd74d 100644 --- a/docs/development/Release.md +++ b/docs/development/Release.md @@ -38,5 +38,11 @@ The Eclipse Bot is able to approve dependencies automatically, if the license ca from maven central. 2. Create the Eclipse IP Issues or ask an Eclipse Commiter to do this for you. +## 4. Update OpenAPI docs + +As part of the [kits documentation provided for docusaurus](../kit/development-view/page00_development_view.md) we provide an OpenAPI reference. +This refers to the [EDC API](https://github.com/eclipse-edc/Connector/tree/main/resources/openapi) and needs to be updated to the current release. +The yaml files found there are then converted with the [docusaurus openapi plugin](https://www.npmjs.com/package/docusaurus-plugin-openapi-docs). + [maven-shield]: https://img.shields.io/badge/Apache%20Maven-URL-blue [maven-url]: https://maven.apache.org diff --git a/docs/kit/adoption-view/images/domain-model.png b/docs/kit/adoption-view/images/domain-model.png new file mode 100644 index 0000000000000000000000000000000000000000..de9562a11cbf896d48e88f70a00254d94e931906 GIT binary patch literal 142498 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úO>|I#cA^or? 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 literal 0 HcmV?d00001 diff --git a/docs/kit/adoption-view/images/edc_architecture.png b/docs/kit/adoption-view/images/edc_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..99b5525eae8358c94213239d15610c004bcadb2b GIT binary patch literal 7768 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 The shown picture illustrates only a generic view of the Domain Model and is not intended to show all aspects of the project. + +## Asset + +An asset represents data (databases, files, cache information, etc.) which should be published and shared between +organizations. For each asset, a [`DataAddress`](#data-address) needs to be resolvable. + +## Data address + +A data address is a pointer into the physical storage location where an asset will be stored. + +## Contract + +A contract always contains one or more [`Assets`](#asset) and a single [`Policy`](#policy). The contract construct is +used to define the arrangement between two parties ("consumer" and "provider"). Regarding this arrangement, the contract +passes several stages which are explained below: + +### Contract definition + + Contract definitions associate a policy with assets. A `ContractDefinition` object contains an access policy, a contract + policy, and an asset selector which links the contract to one or more assets. + +### Contract offer + + The contract offer is a dynamic representation of the [`ContractDefinition`](#contract-definition) + for a specific consumer and serves as protocol's data transfer object (DTO) for a particular contract negotiation. + Contract offers are not persisted and will be regenerated on every request. The connector acting as data provider will + generate contract offers only for contract definitions dedicated to the organization or data space participant + operating the requesting connector acting as data consumer. A contract offer is always related to a single asset of + the `ContractDefinition` object (e.g. for a `ContractDefinition` containing three `Asset` objects, the connector will + generate three `ContractOffer` objects). + +### Contract negotiation + + A `ContractNegotiation` captures the current state of the negotiation of a contract (`ContractOffer` -> + `ContractAgreement`) between two parties. This process is inherently asynchronous. + +### Contract agreement + + A contract agreement represents the agreed-upon terms of access and usage of an asset's data between two data space + participants, including a start and an end date and further relevant information. + +## Policy + +Contract policies represent permitted and prohibited actions over a certain asset. These actions can be limited further +by constraints (temporal or spatial) and duties ("e.g. deletion of the data after 30 days"). + +## Transfer process + +After a successful contract negotiation, a `DataRequest` is sent from a consumer connector to a provider connector to +initiate the data transfer. It references the requested [`Asset`](#asset) and [`ContractAgreement`](#contract-agreement) +as well as information about the [data destination](#data-address). + +Similar to the `ContractNegotiation`, this object captures the current state of a data transfer. This process is +inherently asynchronous, so the `TransferProcess` objects are stored in a backing data store (`TransferProcessStore`). diff --git a/docs/kit/development-view/page00_development_view.md b/docs/kit/development-view/page00_development_view.md new file mode 100644 index 000000000..5a8962382 --- /dev/null +++ b/docs/kit/development-view/page00_development_view.md @@ -0,0 +1,24 @@ +# Development View + +## Project Overview + +TractusX is an initiative of companies under the umbrella of the Eclipse Foundation. +It is a pilot for the larger initiative of CatenaX. +A broader overview of the project can be found on the initiative's [Github page][tractusx-edc-link] +or the homepage of the [Eclipse Foundation](https://projects.eclipse.org/projects/automotive.tractusx). + +## The EDC + +The Eclipse Dataspace Connector is one of the core components facilitating TractusX. + +:::note TractusX EDC or Core EDC? + +This documentation is for TractusX EDC. +It includes the Core EDC with all of its functionality. +However, this core is supplemented by extensions that allow for the use of additional backends and connection types. +Furthermore, the provided Helm charts, build configuration and tests allow for a smoother deployment. +::: + +You can find the repository for the TractusX EDC [here][tractusx-edc-link]. + +[tractusx-edc-link]: https://github.com/eclipse-tractusx/tractusx-edc diff --git a/docs/kit/development-view/page01_eclipse_foundation.md b/docs/kit/development-view/page01_eclipse_foundation.md new file mode 100644 index 000000000..5d865ff76 --- /dev/null +++ b/docs/kit/development-view/page01_eclipse_foundation.md @@ -0,0 +1,35 @@ +# The Eclipse Foundation + +## Eclipse Development Process + +This Eclipse Foundation open project is governed by the Eclipse Foundation +Development Process and operates under the terms of the Eclipse IP Policy. + +* +* + +## Eclipse Contributor Agreement + +In order to be able to contribute to Eclipse Foundation projects you must +electronically sign the Eclipse Contributor Agreement (ECA). + +* + +The ECA provides the Eclipse Foundation with a permanent record that you agree +that each of your contributions will comply with the commitments documented in +the Developer Certificate of Origin (DCO). Having an ECA on file associated with +the email address matching the "Author" field of your contribution's Git commits +fulfills the DCO's requirement that you sign-off on your contributions. + +For more information, please see the Eclipse Committer Handbook: + + +## License + +Code in TractusX EDC is published under the [Apache License](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/LICENSE). + +## Contact + +Contact the project developers via the project's "dev" list. + +* diff --git a/docs/kit/development-view/page02_repository_structure.md b/docs/kit/development-view/page02_repository_structure.md new file mode 100644 index 000000000..7ac6b20d9 --- /dev/null +++ b/docs/kit/development-view/page02_repository_structure.md @@ -0,0 +1,26 @@ +# Repository Structure + +The repository for TractusX EDC can be found [here](https://github.com/eclipse-tractusx/tractusx-edc). +It contains the following components: + +## EDC Extensions + +The core EDC is extensible by design. +TractusX EDC provides such extensions. +These extensions and their documentation are available +[here](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-extensions/README.md). + +## Gradle Files for EDC Builds + +Builds of TractusX EDC are performed via Gradle. +To allow for different configurations, different builds are provided. +For example separate secrets backends are supported, but require separate builds of EDC. +Therefor, different builds are available for both +[data plane](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-dataplane/README.md) +and [control plane](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/README.md), + +## Helm Charts for EDC Deployment + +To facilitate deployment of these different builds and their prerequisites, +Helm charts are provided. The charts and their documentation can be found +[here](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/charts/README.md). diff --git a/docs/kit/development-view/page03_project_structure.md b/docs/kit/development-view/page03_project_structure.md new file mode 100644 index 000000000..43de01233 --- /dev/null +++ b/docs/kit/development-view/page03_project_structure.md @@ -0,0 +1,21 @@ +# Project Structure + +## Issue Tracking + +Issues are maintained in [GitHub Issues](https://github.com/eclipse-tractusx/tractusx-edc/issues). + +## Reporting Vulnerabilities + +Vulnerabilities in the TractusX code base are best reported directly to the +[Eclipse Foundation](https://www.eclipse.org/security/). + +## Git Flow + +The TractusX EDC repository uses a Git Flow, with `main` as the development branch and `releases` as the release branch. +Other branches should follow the naming conventions of `feature/x` or `hotfix/x`, though this is not strictly enforced. + +## Tooling + +We use Java 11 with Gradle for dependencies and builds. +We use [Spotless](https://github.com/diffplug/spotless) for code formatting. +Releases are in the form of Docker containers and Helm charts. diff --git a/docs/kit/operation-view/page00_operation_view.md b/docs/kit/operation-view/page00_operation_view.md new file mode 100644 index 000000000..3c5651b62 --- /dev/null +++ b/docs/kit/operation-view/page00_operation_view.md @@ -0,0 +1,24 @@ +# Software Operation View + +## Introduction + +The following documentation will guide you through the TractusX EDC deployment. +You will be setting up multiple controllers and enabling communication between them. + +:::note TractusX EDC or Core EDC? + +The following guide assumes the use of the TractusX EDC. +It includes the Core EDC with all of its functionality. +However, this core is supplemented by extensions that allow for the use of additional backends and connection types. +Furthermore, the provided Helm charts, build configuration and tests allow for a smoother deployment. +::: + +## Connector Components + +In a usual EDC environment, each participant would operate at least one connector. +Each of these connectors consists of a control plane and a data plane. +The control plane functions as administration layer and is responsible for resource management, contract negotiation and administering data transfer. +The data plane does the heavy lifting of transferring and receiving data streams. + +Each of these planes comes in several variants, allowing for example secrets to be stored in Azure Vault or a Hashicorp Vault. +The setup on the following pages assumes the use of Hashicorp Vault for secrets and PostgreSQL for data storage. diff --git a/docs/kit/operation-view/page02_technical_prerequisites.md b/docs/kit/operation-view/page02_technical_prerequisites.md new file mode 100644 index 000000000..83ed7e76b --- /dev/null +++ b/docs/kit/operation-view/page02_technical_prerequisites.md @@ -0,0 +1,43 @@ +# Technical Prerequisites + +## Obtaining Releases + +The most recent release of TractusX EDC can be obtained under `https://github.com/eclipse-tractusx/tractusx-edc/releases`. +To create your own build, you can clone the repository at `https://github.com/eclipse-tractusx/tractusx-edc` and consult the provided README.md. +This can be useful if you want to use non-standard extensions or configuration. + +## Container Environment + +TractusX releases come in the form of Docker containers and corresponding Helm charts. +As such, recent versions of the following are required. + +- Docker +- Kubernetes +- Helm + +Seeing as these are standard tools, TractusX EDC will run on any cloud environment that can accept Helm charts. + +## Backend Dependencies + +The EDC requires backend services for persistence of data and secrets. The following backends are currently supported. + +Data Storage: + +- PostgreSQL database +- In memory database + +Secret Storage: + +- Hashicorp Vault +- Azure Vault + +The default setup assumes data storage via PostgreSQL database. +In memory storage is only recommended for running tests. +Hashicorp Vault is the default secret provider, because it is platform-agnostic. + +Helm charts are provided to set up these services locally. +**These are not suited for production environments.** + +## All-in-one deployment + +An all-in-one deployment is no longer in scope and will not be provided. diff --git a/docs/kit/operation-view/page03_local_setup_controlplane.md b/docs/kit/operation-view/page03_local_setup_controlplane.md new file mode 100644 index 000000000..93c52606f --- /dev/null +++ b/docs/kit/operation-view/page03_local_setup_controlplane.md @@ -0,0 +1,141 @@ +# Setting up a local EDC Control Plane + +## Basics + +The EDC is split into control and data plane. +The data plane handles the actual data transfer between parties. +The control plane manages the following: + +- Resource Management (e.g. Assets, Policies & Contract Definitions CRUD) +- Contract Offering & Contract Negotiation +- Data Transfer Coordination / Management + +The EDC control plane can run as a single container on your local machine. +The following is a short overview of the necessary steps to start up the default configuration. + +## Building + +TractusX EDC is build with Gradle. The following command creates the default control plane as a docker container: + +```shell +./gardlew :edc-controlplane:edc-controlplane-postgresql-hashicorp-vault:dockerize +``` + +## Example Configuration + +The following commands can be used to create the necessary configuration files for the EDC container. +They assume sane - but unsafe - defaults. An explanation of the respective parameters can be found [here](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-controlplane/edc-controlplane-postgresql-hashicorp-vault/README.md). + +:::danger +The following configuration is for testing purposes only. Do not use it in production. +::: + +### Example configuration.properties + +```shell +# Create configuration.properties +export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) +cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} + +web.http.default.port=8080 +web.http.default.path=/api +web.http.management.port=8181 +web.http.management.path=/data +web.http.control.port=9999 +web.http.control.path=/api/controlplane/control +web.http.protocol.port=8282 +web.http.protocol.path=/api/v1/ids + +edc.receiver.http.dynamic.endpoint=http://backend-service + +edc.ids.title=Eclipse Dataspace Connector +edc.ids.description=Eclipse Dataspace Connector +edc.ids.id=urn:connector:edc +edc.ids.security.profile=base +edc.ids.endpoint=http://localhost:8282/api/v1/ids +edc.ids.maintainer=http://localhost +edc.ids.curator=http://localhost +edc.ids.catalog.id=urn:catalog:default +ids.webhook.address=http://localhost:8282/api/v1/ids + +edc.hostname=localhost + +edc.api.auth.key=password + +# OAuth / DAPS related configuration +edc.oauth.token.url=https://daps.catena-x.net +edc.oauth.certificate.alias=key-to-daps-certificate-in-keyvault +edc.oauth.private.key.alias=key-to-private-key-in-keyvault +edc.oauth.client.id=daps-oauth-client-id + +# HashiCorp vault related configuration +edc.vault.hashicorp.url=http://vault +edc.vault.hashicorp.token=55555555-6666-7777-8888-999999999999 +edc.vault.hashicorp.timeout.seconds=30 + +# Control- / Data- Plane configuration +edc.transfer.proxy.endpoint=http://dataplane-public-endpoint/public +edc.transfer.proxy.token.signer.privatekey.alias=token-signer-private-key + +# Postgresql related configuration +edc.datasource.asset.name=asset +edc.datasource.asset.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_asset +edc.datasource.asset.user=user +edc.datasource.asset.password=pass +edc.datasource.contractdefinition.name=contractdefinition +edc.datasource.contractdefinition.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractdefinition +edc.datasource.contractdefinition.user=user +edc.datasource.contractdefinition.password=pass +edc.datasource.contractnegotiation.name=contractnegotiation +edc.datasource.contractnegotiation.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_contractnegotiation +edc.datasource.contractnegotiation.user=user +edc.datasource.contractnegotiation.password=pass +edc.datasource.policy.name=policy +edc.datasource.policy.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_policy +edc.datasource.policy.user=user +edc.datasource.policy.password=pass +edc.datasource.transferprocess.name=transferprocess +edc.datasource.transferprocess.url=jdbc:postgresql://postgres.svc.cluster.local:5432/edc_transferprocess +edc.datasource.transferprocess.user=user +edc.datasource.transferprocess.password=pass +EOF +``` + +### Example logging.properties + +```shell +# Create logging.properties +export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) +cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} +.level=INFO +org.eclipse.edc.level=ALL +handlers=java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +EOF +``` + +### Example opentelemetry.properties + +```shell +# Create opentelemetry.properties +export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) +cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} +otel.javaagent.enabled=false +otel.javaagent.debug=false +EOF +``` + +## Running the Control Plane + +Once the configuration is created, the container can be run directly via docker. + +```shell +docker run \ + -p 8080:8080 -p 8181:8181 -p 8182:8182 -p 8282:8282 -p 9090:9090 -p 9999:9999 \ + -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ + -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ + -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ + -i edc-controlplane-postgresql-hashicorp-vault:latest +``` diff --git a/docs/kit/operation-view/page04_local_setup_dataplane.md b/docs/kit/operation-view/page04_local_setup_dataplane.md new file mode 100644 index 000000000..729a2371b --- /dev/null +++ b/docs/kit/operation-view/page04_local_setup_dataplane.md @@ -0,0 +1,98 @@ +# Setting up a local EDC Data Plane + +## Basics + +The EDC is split into control and data plane. +The data plane handles the actual data transfer between parties. +The control plane manages the following: + +- Resource Management (e.g. Assets, Policies & Contract Definitions CRUD) +- Contract Offering & Contract Negotiation +- Data Transfer Coordination / Management + +The EDC data plane can run as a single container on your local machine. +The following is a short overview of the necessary steps to start up the default configuration. + +## Building + +TractusX EDC is build with Gradle. The following command creates the default data plane as a docker container: + +```shell +./gardlew :edc-dataplane:edc-dataplane-hashicorp-vault:dockerize +``` + +## Example Configuration + +The following commands can be used to create the necessary configuration files for the EDC container. +They assume sane - but unsafe - defaults. An explanation of the respective parameters can be found [here](https://github.com/eclipse-tractusx/tractusx-edc/blob/main/edc-dataplane/edc-dataplane-hashicorp-vault/README.md). + +:::danger + +The following configuration is for testing purposes only. Do not use it in production. +::: + +### Example configuration.properties + +```shell +# Create configuration.properties +export CONFIGURATION_PROPERTIES_FILE=$(mktemp /tmp/configuration.properties.XXXXXX) +cat << 'EOF' > ${CONFIGURATION_PROPERTIES_FILE} + +web.http.default.port=8080 +web.http.default.path=/api +web.http.public.port=8185 +web.http.public.path=/public +web.http.control.port=9999 +web.http.control.path=/api/dataplane/control + +# Validation endpoint of controlplane +edc.dataplane.token.validation.endpoint=http://controlplane:9999/api/controlplane/control/token + +# EDC hostname +edc.hostname=localhost + +# HashiCorp vault related configuration +edc.vault.hashicorp.url=http://vault +edc.vault.hashicorp.token=55555555-6666-7777-8888-999999999999 +edc.vault.hashicorp.timeout.seconds=30 +EOF +``` + +### Example logging.properties + +```shell +# Create logging.properties +export LOGGING_PROPERTIES_FILE=$(mktemp /tmp/logging.properties.XXXXXX) +cat << 'EOF' > ${LOGGING_PROPERTIES_FILE} +.level=INFO +org.eclipse.edc.level=ALL +handlers=java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n +EOF +``` + +### Example opentelemetry.properties + +```shell +# Create opentelemetry.properties +export OPENTELEMETRY_PROPERTIES_FILE=$(mktemp /tmp/opentelemetry.properties.XXXXXX) +cat << 'EOF' > ${OPENTELEMETRY_PROPERTIES_FILE} +otel.javaagent.enabled=true +otel.javaagent.debug=false +EOF +``` + +## Running + +Once the configuration is created, the container can be run directly via docker. + +```shell +docker run \ + -p 8080:8080 -p 8185:8185 -p 9999:9999 -p 9090:9090 \ + -v ${CONFIGURATION_PROPERTIES_FILE:-/dev/null}:/app/configuration.properties \ + -v ${LOGGING_PROPERTIES_FILE:-/dev/null}:/app/logging.properties \ + -v ${OPENTELEMETRY_PROPERTIES_FILE:-/dev/null}:/app/opentelemetry.properties \ + -i edc-dataplane-hashicorp-vault:latest +``` diff --git a/docs/kit/operation-view/page06_kubernetes_setup.md b/docs/kit/operation-view/page06_kubernetes_setup.md new file mode 100644 index 000000000..67930be5e --- /dev/null +++ b/docs/kit/operation-view/page06_kubernetes_setup.md @@ -0,0 +1,22 @@ +# Setup in Kubernetes via Helm + +## Introduction + +While the local setup described earlier is sufficient to test basic EDC functionality, it is not appropriate for any actual environments. +For a more complete setup, Helm charts are provided. + +## Setup + +To set up an example environment, you can use the following Helm commands: + +```shell +helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev +helm install my-release tractusx-edc/tractusx-connector-memory --version 0.3.3 +``` + +## Configuration + +The Helm chart used above can be found [here](https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts). +Configuring that deployment requires the same parameters as the local setup described previously. +Helm expects these parameters in the relevant `values.yaml`. +Similar example configurations can be found with the respective charts under the above link. diff --git a/docs/kit/operation-view/page08_api.md b/docs/kit/operation-view/page08_api.md new file mode 100644 index 000000000..9f45abe14 --- /dev/null +++ b/docs/kit/operation-view/page08_api.md @@ -0,0 +1,64 @@ +# EDC API Examples + +## API Spec + +The API spec of the EDC is constantly evolving. +The full API documentation for each release can be viewed on [management-api](../development-view/openAPI/management-api/management-api.info.mdx). +The following are some example API calls for common use cases. +They assume the default parameters from the previous local setup. + +## Create an Asset + +All objects in EDC are created by posting their JSON-serialized representation to the respective API input. +Since most EDC objects are rather openly defined, most of the properties provided depend on the need of the individual user. +Assets are no exception here. + +URL + +```http request +POST http://localhost:8080/api/v1/assets/ +``` + +Body + +```json +{ + "asset": { + "id": "asset1", + "properties": { + "exampleProperty": "exampleValue" + } + }, + "dataAddress": { + "properties": { + "baseUrl": "https://path.to/the_asset", + "type": "HttpData" + } + } +} +``` + +## Request an Asset Catalog + +To inspect the assets available to an EDC connector, we request its catalog. + +URL + +```http request +POST http://localhost:8080/api/v1/catalog/request +``` + +Body + +```json +{ + "providerUrl": "www.example.provider", + "querySpec": { + "filter": "AvailableWithPolicyXYZ", + "limit": 0, + "offset": 0, + "sortField": "id", + "sortOrder": "ASC" + } +} +``` diff --git a/docs/kit/operation-view/page09_upgrading.md b/docs/kit/operation-view/page09_upgrading.md new file mode 100644 index 000000000..e63a55526 --- /dev/null +++ b/docs/kit/operation-view/page09_upgrading.md @@ -0,0 +1,20 @@ +# Upgrading TractusX EDC + +Among the goals of TractusX EDC is making EDC upgrades as painless as possible. +The changes in each release are documented [here](https://github.com/eclipse-tractusx/tractusx-edc/tree/main/docs/migration). +Usually there are only three steps to each upgrade. + +## Database Migration + +Database migration is simple to accomplish with a PostgreSQL backend. +The [PostgreSQL Migration Extension](https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/postgresql-migration) is the preferred approach. +Alternatively, the `.sql` files therein can be used to manually update the database schema. + +## Updating EDC + +The easy part of the upgrade process is to simply switch the outdated EDC containers with their newer counterparts. + +## Updating Settings + +Check the newest [Migration Documents](https://github.com/eclipse-tractusx/tractusx-edc/tree/develop/docs/migration) +for any changes to the settings structure and apply them to your settings. diff --git a/docs/kit/operation-view/page10_extensions.md b/docs/kit/operation-view/page10_extensions.md new file mode 100644 index 000000000..dce82d153 --- /dev/null +++ b/docs/kit/operation-view/page10_extensions.md @@ -0,0 +1,44 @@ +# EDC Extensions + +The following extensions provide additional functionality to the core EDC. +They are currently only available in TractusX EDC. + +## Business Partner Validation + +This extension allows for validation of business partners within the access policy. + +## Control Plane Adapter + +The goal of this extension is to simplify the process of retrieving data out of EDC. +It returns `EndpointDataReference` object, hiding all the communication details for contract offers, +contract negotiation, transfer process and retrieving the underlying data through the data-planes. + +## CX OAuth2 + +This extension enables OAuth2 authentication between EDC connectors, +instead of the more complex authentication flow used by core EDC. + +## Data Encryption + +The EDC encrypts sensitive information inside a token it sends to other applications (potentially cross-company). +This extension implements the encryption of this data and should be used with secure keys and algorithms at all times. + +## Data Plane Selector + +This control plane extension makes it possible to configure one or more data plane instances. +During a transfer the control plane will look for an instance with matching capabilities to transfer data. + +## Hashicorp Vault + +This extension allows for usage of Hashicorp Vault for secret storage. +It is the default used in TractusX EDC. + +## PostrgreSQL Migration + +While the core EDC is able to interact with PostgreSQL databases, +it does not automate migrations between schema versions. +This extension adds that functionality. + +## Transfer Process SFTP + +This extension allows for the use of SFTP backends for the data plane (but is not included in the provided control- and data plane). From 01b426747d2c4d112824f4d1d6b2cf2126ebf2bb Mon Sep 17 00:00:00 2001 From: stephanbcbauer <84396022+stephanbcbauer@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:01:13 +0200 Subject: [PATCH 103/132] feat: add GitHub workflow to automaticly add features to project (#264) --- .github/workflows/add-to-project.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/add-to-project.yml diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml new file mode 100644 index 000000000..00ffae871 --- /dev/null +++ b/.github/workflows/add-to-project.yml @@ -0,0 +1,17 @@ +name: Add to Project + +on: + issues: + types: [ opened ] + +jobs: + add-to-project: + runs-on: ubuntu-latest + steps: + - name: Add issue to project + uses: actions/add-to-project@v0.5.0 + with: + project-url: https://github.com/orgs/eclipse-tractusx/projects/5 + github-token: ${{ secrets.GITHUB_TOKEN }} + labeled: bug + label-operator: NOT From 50ef73befeea82a9e6f9c01f304fb23452f84f23 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:37:06 +0200 Subject: [PATCH 104/132] feature: refactor the main `tractusx-connector` chart (#230) * chore: Add 0.3.3 to, and fix markdown in CHANGELOG.md (#252) * feature: create new tractusx chart with Hashicorp and Postgres * lint * fix deployment test * updated urls * pr remarks * construct readiness URL directly in the test pod * Apply suggestions from code review Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) * Update charts/tractusx-connector/templates/tests/test-controlplane-readiness.yaml Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --------- Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- .../actions/run-deployment-test/action.yml | 7 - .github/workflows/deployment-test.yaml | 39 +- .gitignore | 4 + .../.helmignore | 0 charts/tractusx-connector-app/Chart.yaml | 70 +++ charts/tractusx-connector-app/README.md | 237 ++++++++ charts/tractusx-connector-app/example.yaml | 190 +++++++ charts/tractusx-connector-app/values.yaml | 532 ++++++++++++++++++ charts/tractusx-connector/Chart.yaml | 6 +- charts/tractusx-connector/README.md | 15 +- .../templates/deployment-controlplane.yaml | 39 +- .../templates/deployment-dataplane.yaml | 29 +- .../tests/test-controlplane-readiness.yaml | 15 + .../tests/test-dataplane-readiness.yaml | 15 + charts/tractusx-connector/values.yaml | 12 +- .../helm/test-infrastructure/Chart.yaml | 21 - .../helm/test-infrastructure/values.yaml | 140 ----- 17 files changed, 1122 insertions(+), 249 deletions(-) rename charts/{tractusx-connector => tractusx-connector-app}/.helmignore (100%) create mode 100644 charts/tractusx-connector-app/Chart.yaml create mode 100644 charts/tractusx-connector-app/README.md create mode 100644 charts/tractusx-connector-app/example.yaml create mode 100644 charts/tractusx-connector-app/values.yaml create mode 100644 charts/tractusx-connector/templates/tests/test-controlplane-readiness.yaml create mode 100644 charts/tractusx-connector/templates/tests/test-dataplane-readiness.yaml diff --git a/.github/actions/run-deployment-test/action.yml b/.github/actions/run-deployment-test/action.yml index 9f4b40d58..acd430d49 100644 --- a/.github/actions/run-deployment-test/action.yml +++ b/.github/actions/run-deployment-test/action.yml @@ -43,13 +43,6 @@ runs: using: "composite" steps: - uses: actions/checkout@v3.3.0 - - - name: Cache ContainerD Image Layers - uses: actions/cache@v3 - with: - path: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs - key: ${{ runner.os }}-io.containerd.snapshotter.v1.overlayfs - - uses: ./.github/actions/setup-java - name: Build docker images diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index 8e75ae31e..babb24a8e 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -44,8 +44,19 @@ concurrency: cancel-in-progress: true jobs: - deployment-test-memory: + + test-prepare: + runs-on: ubuntu-latest + steps: + - name: Cache ContainerD Image Layers + uses: actions/cache@v3 + with: + path: /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs + key: ${{ runner.os }}-io.containerd.snapshotter.v1.overlayfs + + test-in-memory: runs-on: ubuntu-latest + needs: test-prepare steps: - uses: actions/checkout@v3.3.0 - uses: ./.github/actions/run-deployment-test @@ -63,4 +74,28 @@ jobs: kubectl rollout status deployment tx-inmem # execute the helm test - helm test tx-inmem + helm test tx-inmem --logs + + test-hashicorp-postgres: + runs-on: ubuntu-latest + needs: test-prepare + steps: + - name: Checkout + uses: actions/checkout@v3.3.0 + - uses: ./.github/actions/run-deployment-test + name: "Run deployment test using KinD and Helm" + with: + imagename: "edc-controlplane-postgresql-hashicorp-vault edc-dataplane-hashicorp-vault" + rootDir: "." + helm_command: |- + helm install tx-prod charts/tractusx-connector-app \ + -f charts/tractusx-connector-app/example.yaml \ + --dependency-update \ + --wait-for-jobs --timeout=120s + + # wait for the pod to become ready + kubectl rollout status deployment tx-prod-runtime-controlplane + kubectl rollout status deployment tx-prod-runtime-dataplane + + # execute the helm test + helm test tx-prod --logs diff --git a/.gitignore b/.gitignore index cb53525fa..9f9952efe 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,7 @@ buildNumber.properties .mvn/timing.properties # https://github.com/takari/maven-wrapper#usage-without-binary-jar .mvn/wrapper/maven-wrapper.jar + +### Helm +**/*.lock +**/*.tgz diff --git a/charts/tractusx-connector/.helmignore b/charts/tractusx-connector-app/.helmignore similarity index 100% rename from charts/tractusx-connector/.helmignore rename to charts/tractusx-connector-app/.helmignore diff --git a/charts/tractusx-connector-app/Chart.yaml b/charts/tractusx-connector-app/Chart.yaml new file mode 100644 index 000000000..cdd48977d --- /dev/null +++ b/charts/tractusx-connector-app/Chart.yaml @@ -0,0 +1,70 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +apiVersion: v2 +name: tractusx-connector-app +description: | + A Helm chart for Tractus-X Eclipse Data Space Connector Application. This includes the runtime, which consists of a control plane + and a data plane, and all third-party services such as PostgreSQL and HashiCorp Vault. + + This chart is intended to be used as self-contained deployment, which only requires an external DAPS instance. +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.3.2 +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.3.2" +home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-app +sources: + - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-app + +dependencies: + # EDC Connector Runtime (ControlPlane + DataPlane) + - name: tractusx-connector + version: "0.3.3" + alias: runtime + repository: "file://../tractusx-connector" + + # HashiCorp Vault + - name: vault + alias: vault + version: 0.20.0 + repository: https://helm.releases.hashicorp.com + + # PostgreSQL + - name: postgresql + alias: postgresql + version: 12.1.6 + repository: https://charts.bitnami.com/bitnami diff --git a/charts/tractusx-connector-app/README.md b/charts/tractusx-connector-app/README.md new file mode 100644 index 000000000..56af7fdcf --- /dev/null +++ b/charts/tractusx-connector-app/README.md @@ -0,0 +1,237 @@ +# tractusx-connector-app + +![Version: 0.3.2](https://img.shields.io/badge/Version-0.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.2](https://img.shields.io/badge/AppVersion-0.3.2-informational?style=flat-square) + +A Helm chart for Tractus-X Eclipse Data Space Connector Application. This includes the runtime, which consists of a control plane +and a data plane, and all third-party services such as PostgreSQL and HashiCorp Vault. + +This chart is intended to be used as self-contained deployment, which only requires an external DAPS instance. + +**Homepage:** + +## Source Code + +* + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| file://../tractusx-connector | runtime(tractusx-connector) | 0.3.2 | +| https://charts.bitnami.com/bitnami | postgresql(postgresql) | 12.1.6 | +| https://helm.releases.hashicorp.com | vault(vault) | 0.20.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| customLabels | object | `{}` | | +| fullnameOverride | string | `""` | | +| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| nameOverride | string | `""` | | +| runtime.backendService.httpProxyTokenReceiverUrl | string | `""` | | +| runtime.controlplane.affinity | object | `{}` | | +| runtime.controlplane.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | +| runtime.controlplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| runtime.controlplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| runtime.controlplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| runtime.controlplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| runtime.controlplane.debug.enabled | bool | `false` | | +| runtime.controlplane.debug.port | int | `1044` | | +| runtime.controlplane.debug.suspendOnStart | bool | `false` | | +| runtime.controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"observability":{"insecure":true,"path":"/observability","port":8085},"protocol":{"path":"/api/v1/ids","port":8084}}` | endpoints of the control plane | +| runtime.controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | +| runtime.controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | +| runtime.controlplane.endpoints.control.port | int | `8083` | port for incoming api calls | +| runtime.controlplane.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | +| runtime.controlplane.endpoints.default.path | string | `"/api"` | path for incoming api calls | +| runtime.controlplane.endpoints.default.port | int | `8080` | port for incoming api calls | +| runtime.controlplane.endpoints.management | object | `{"authKey":"","path":"/management","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | +| runtime.controlplane.endpoints.management.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | +| runtime.controlplane.endpoints.management.path | string | `"/management"` | path for incoming api calls | +| runtime.controlplane.endpoints.management.port | int | `8081` | port for incoming api calls | +| runtime.controlplane.endpoints.metrics | object | `{"path":"/metrics","port":9090}` | metrics api, used for application metrics, must not be internet facing | +| runtime.controlplane.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | +| runtime.controlplane.endpoints.metrics.port | int | `9090` | port for incoming api calls | +| runtime.controlplane.endpoints.observability | object | `{"insecure":true,"path":"/observability","port":8085}` | observability api with unsecured access, must not be internet facing | +| runtime.controlplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | +| runtime.controlplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | +| runtime.controlplane.endpoints.observability.port | int | `8085` | port for incoming API calls | +| runtime.controlplane.endpoints.protocol | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | +| runtime.controlplane.endpoints.protocol.path | string | `"/api/v1/ids"` | path for incoming api calls | +| runtime.controlplane.endpoints.protocol.port | int | `8084` | port for incoming api calls | +| runtime.controlplane.env | object | `{}` | | +| runtime.controlplane.envConfigMapNames | list | `[]` | | +| runtime.controlplane.envSecretNames | list | `[]` | | +| runtime.controlplane.envValueFrom | object | `{}` | | +| runtime.controlplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| runtime.controlplane.image.repository | string | `""` | Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically | +| runtime.controlplane.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | +| runtime.controlplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| runtime.controlplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| runtime.controlplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| runtime.controlplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| runtime.controlplane.ingresses[0].enabled | bool | `false` | | +| runtime.controlplane.ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | +| runtime.controlplane.ingresses[0].hostname | string | `"edc-control.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| runtime.controlplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| runtime.controlplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| runtime.controlplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | +| runtime.controlplane.ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | +| runtime.controlplane.ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| runtime.controlplane.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| runtime.controlplane.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| runtime.controlplane.ingresses[1].enabled | bool | `false` | | +| runtime.controlplane.ingresses[1].endpoints | list | `["management","control"]` | EDC endpoints exposed by this ingress resource | +| runtime.controlplane.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| runtime.controlplane.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| runtime.controlplane.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| runtime.controlplane.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | +| runtime.controlplane.initContainers | list | `[]` | | +| runtime.controlplane.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | +| runtime.controlplane.internationalDataSpaces.curator | string | `""` | | +| runtime.controlplane.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | +| runtime.controlplane.internationalDataSpaces.id | string | `"TXDC"` | | +| runtime.controlplane.internationalDataSpaces.maintainer | string | `""` | | +| runtime.controlplane.internationalDataSpaces.title | string | `""` | | +| runtime.controlplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| runtime.controlplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| runtime.controlplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | +| runtime.controlplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| runtime.controlplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| runtime.controlplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| runtime.controlplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| runtime.controlplane.nodeSelector | object | `{}` | | +| runtime.controlplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | +| runtime.controlplane.podAnnotations | object | `{}` | additional annotations for the pod | +| runtime.controlplane.podLabels | object | `{}` | additional labels for the pod | +| runtime.controlplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| runtime.controlplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| runtime.controlplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| runtime.controlplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| runtime.controlplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | +| runtime.controlplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| runtime.controlplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| runtime.controlplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | +| runtime.controlplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a readiness check every 10 seconds | +| runtime.controlplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| runtime.controlplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| runtime.controlplane.replicaCount | int | `1` | | +| runtime.controlplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | +| runtime.controlplane.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | +| runtime.controlplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| runtime.controlplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| runtime.controlplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | +| runtime.controlplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| runtime.controlplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | +| runtime.controlplane.service.annotations | object | `{}` | | +| runtime.controlplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| runtime.controlplane.tolerations | list | `[]` | | +| runtime.controlplane.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | +| runtime.controlplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | +| runtime.controlplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | +| runtime.daps.clientId | string | `""` | | +| runtime.daps.paths.jwks | string | `"/jwks.json"` | | +| runtime.daps.paths.token | string | `"/token"` | | +| runtime.daps.url | string | `""` | | +| runtime.dataplane.affinity | object | `{}` | | +| runtime.dataplane.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | +| runtime.dataplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | +| runtime.dataplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | +| runtime.dataplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | +| runtime.dataplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | +| runtime.dataplane.aws.accessKeyId | string | `""` | | +| runtime.dataplane.aws.endpointOverride | string | `""` | | +| runtime.dataplane.aws.secretAccessKey | string | `""` | | +| runtime.dataplane.debug.enabled | bool | `false` | | +| runtime.dataplane.debug.port | int | `1044` | | +| runtime.dataplane.debug.suspendOnStart | bool | `false` | | +| runtime.dataplane.endpoints.control.path | string | `"/api/dataplane/control"` | | +| runtime.dataplane.endpoints.control.port | int | `8083` | | +| runtime.dataplane.endpoints.default.path | string | `"/api"` | | +| runtime.dataplane.endpoints.default.port | int | `8080` | | +| runtime.dataplane.endpoints.metrics.path | string | `"/metrics"` | | +| runtime.dataplane.endpoints.metrics.port | int | `9090` | | +| runtime.dataplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | +| runtime.dataplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | +| runtime.dataplane.endpoints.observability.port | int | `8085` | port for incoming API calls | +| runtime.dataplane.endpoints.public.path | string | `"/api/public"` | | +| runtime.dataplane.endpoints.public.port | int | `8081` | | +| runtime.dataplane.env | object | `{}` | | +| runtime.dataplane.envConfigMapNames | list | `[]` | | +| runtime.dataplane.envSecretNames | list | `[]` | | +| runtime.dataplane.envValueFrom | object | `{}` | | +| runtime.dataplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | +| runtime.dataplane.image.repository | string | `""` | Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically | +| runtime.dataplane.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | +| runtime.dataplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | +| runtime.dataplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | +| runtime.dataplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | +| runtime.dataplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | +| runtime.dataplane.ingresses[0].enabled | bool | `false` | | +| runtime.dataplane.ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | +| runtime.dataplane.ingresses[0].hostname | string | `"edc-data.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | +| runtime.dataplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | +| runtime.dataplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | +| runtime.dataplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | +| runtime.dataplane.initContainers | list | `[]` | | +| runtime.dataplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| runtime.dataplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| runtime.dataplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | +| runtime.dataplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| runtime.dataplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| runtime.dataplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| runtime.dataplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | +| runtime.dataplane.nodeSelector | object | `{}` | | +| runtime.dataplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | +| runtime.dataplane.podAnnotations | object | `{}` | additional annotations for the pod | +| runtime.dataplane.podLabels | object | `{}` | additional labels for the pod | +| runtime.dataplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | +| runtime.dataplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | +| runtime.dataplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | +| runtime.dataplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | +| runtime.dataplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | +| runtime.dataplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | +| runtime.dataplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | +| runtime.dataplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | +| runtime.dataplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | +| runtime.dataplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | +| runtime.dataplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | +| runtime.dataplane.replicaCount | int | `1` | | +| runtime.dataplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | +| runtime.dataplane.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | +| runtime.dataplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | +| runtime.dataplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | +| runtime.dataplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | +| runtime.dataplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | +| runtime.dataplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | +| runtime.dataplane.service.port | int | `80` | | +| runtime.dataplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | +| runtime.dataplane.tolerations | list | `[]` | | +| runtime.dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | +| runtime.dataplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | +| runtime.dataplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | +| runtime.postgresql.enabled | bool | `false` | | +| runtime.postgresql.jdbcUrl | string | `""` | | +| runtime.postgresql.password | string | `""` | | +| runtime.postgresql.username | string | `""` | | +| runtime.serviceAccount.annotations | object | `{}` | | +| runtime.serviceAccount.create | bool | `true` | | +| runtime.serviceAccount.imagePullSecrets | list | `[]` | Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | +| runtime.serviceAccount.name | string | `""` | | +| runtime.vault.hashicorp.enabled | bool | `true` | | +| runtime.vault.hashicorp.healthCheck.enabled | bool | `true` | | +| runtime.vault.hashicorp.healthCheck.standbyOk | bool | `true` | | +| runtime.vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | +| runtime.vault.hashicorp.paths.secret | string | `"/v1/secret"` | | +| runtime.vault.hashicorp.timeout | int | `30` | | +| runtime.vault.hashicorp.token | string | `""` | | +| runtime.vault.hashicorp.url | string | `""` | | +| runtime.vault.secretNames.dapsPrivateKey | string | `"daps-private-key"` | | +| runtime.vault.secretNames.dapsPublicKey | string | `"daps-public-key"` | | +| runtime.vault.secretNames.transferProxyTokenEncryptionAesKey | string | `"transfer-proxy-token-encryption-aes-key"` | | +| runtime.vault.secretNames.transferProxyTokenSignerPrivateKey | string | `"transfer-proxy-token-signer-private-key"` | | +| runtime.vault.secretNames.transferProxyTokenSignerPublicKey | string | `"transfer-proxy-token-signer-public-key"` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/tractusx-connector-app/example.yaml b/charts/tractusx-connector-app/example.yaml new file mode 100644 index 000000000..280121252 --- /dev/null +++ b/charts/tractusx-connector-app/example.yaml @@ -0,0 +1,190 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +## This file can be used to verify that the chart is working properly. It provides an exemplary configuration +## that is intended to be used with the supporting infrastructure. +## 1. install DAPS: +## helm install infrastructure edc-tests/deployment/src/main/resources/helm/test-infrastructure --wait-for-jobs +## +## 2. install the connector plus its third-party dependencies (HashiCorp Vault and Postgres): +## helm install tx-prod charts/tractusx-connector-app -f charts/tractusx-connector-app/example.yaml --dependency-update + +fullnameOverride: tx-prod + +################################ +# EDC ControlPlane + DataPlane # +################################ +runtime: + controlplane: + service: + type: NodePort + endpoints: + management: + authKey: password + image: + pullPolicy: Never + tag: "latest" + repository: "edc-controlplane-postgresql-hashicorp-vault" + securityContext: + # avoids some errors in the log: cannot write temp files of large multipart requests when R/O + readOnlyRootFilesystem: false + + dataplane: + image: + pullPolicy: Never + tag: "latest" + repository: "edc-dataplane-hashicorp-vault" + + securityContext: + # avoids some errors in the log: cannot write temp files of large multipart requests when R/O + readOnlyRootFilesystem: false + + aws: + endpointOverride: http://minio:9000 + secretAccessKey: qwerty123 + accessKeyId: qwerty123 + + postgresql: + username: user + password: password + jdbcUrl: jdbc:postgresql://postgresql:5432/edc + + vault: + hashicorp: + url: http://vault:8200 + token: root + + secretNames: + transferProxyTokenSignerPublicKey: daps-crt + transferProxyTokenSignerPrivateKey: daps-key + transferProxyTokenEncryptionAesKey: aes-keys + dapsPrivateKey: daps-key + dapsPublicKey: daps-crt + + # this must be set through CLI args: --set vault.secrets=$YOUR_VAULT_SECRETS where YOUR_VAULT_SECRETS should + # be a string in the format "key1:secret1;key2:secret2;..." + secrets: + + daps: + url: "http://ids-daps:4567" + clientId: "E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65" + + backendService: + httpProxyTokenReceiverUrl: "http://backend:8080" + +############## +# POSTGRESQL # +############## +postgresql: + fullnameOverride: "postgresql" + primary: + persistence: + enabled: false + readReplicas: + persistence: + enabled: false + auth: + database: "edc" + username: "user" + password: "password" + +######### +# VAULT # +######### +vault: + fullnameOverride: "vault" + injector: + enabled: false + server: + dev: + enabled: true + devRootToken: "root" + # Must be the same certificate that is configured in section 'ids-daps' + postStart: + - "sh" + - "-c" + - | + { + + sleep 5 + + /bin/vault kv put secret/sokrates/data-encryption-aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== + + cat << EOF | /bin/vault kv put secret/daps-key content=- + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM + wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ + FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 + 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ + ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE + sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc + RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z + d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm + hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm + cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh + FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F + MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e + uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb + ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 + z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 + h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ + vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB + 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 + hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 + dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 + Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 + IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT + 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr + 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 + u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B + AjWFbUiBCFOo+gpAFcQGrkOQHA== + -----END PRIVATE KEY----- + EOF + + cat << EOF | /bin/vault kv put secret/daps-crt content=- + -----BEGIN CERTIFICATE----- + MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ + TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK + DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD + DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw + 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa + llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV + grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 + PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 + ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT + MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH + LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd + iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E + 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 + S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r + uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB + UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + -----END CERTIFICATE----- + EOF + + /bin/vault kv put secret/aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== + + } diff --git a/charts/tractusx-connector-app/values.yaml b/charts/tractusx-connector-app/values.yaml new file mode 100644 index 000000000..98a62ef06 --- /dev/null +++ b/charts/tractusx-connector-app/values.yaml @@ -0,0 +1,532 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + + +--- +# Default values for eclipse-dataspace-connector. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +fullnameOverride: "" +nameOverride: "" + +# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) +imagePullSecrets: [] + +customLabels: {} + +runtime: + controlplane: + image: + # -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically + repository: "" + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "" + initContainers: [] + debug: + enabled: false + port: 1044 + suspendOnStart: false + internationalDataSpaces: + id: TXDC + description: Tractus-X Eclipse IDS Data Space Connector + title: "" + maintainer: "" + curator: "" + catalogId: TXDC-Catalog + livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first liveness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + readinessProbe: + # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first readiness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a readiness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # -- endpoints of the control plane + endpoints: + # -- default api for health checks, should not be added to any ingress + default: + # -- port for incoming api calls + port: 8080 + # -- path for incoming api calls + path: /api + # -- data management api, used by internal users, can be added to an ingress and must not be internet facing + management: + # -- port for incoming api calls + port: 8081 + # -- path for incoming api calls + path: /management + # -- authentication key, must be attached to each 'X-Api-Key' request header + authKey: "" + # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not + control: + # -- port for incoming api calls + port: 8083 + # -- path for incoming api calls + path: /control + # -- ids api, used for inter connector communication and must be internet facing + protocol: + # -- port for incoming api calls + port: 8084 + # -- path for incoming api calls + path: /api/v1/ids + # -- metrics api, used for application metrics, must not be internet facing + metrics: + # -- port for incoming api calls + port: 9090 + # -- path for incoming api calls + path: /metrics + # -- observability api with unsecured access, must not be internet facing + observability: + # -- port for incoming API calls + port: 8085 + # -- observability api, provides /health /readiness and /liveness endpoints + path: /observability + # -- allow or disallow insecure access, i.e. access without authentication + insecure: true + service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + annotations: {} + # -- additional labels for the pod + podLabels: {} + # -- additional annotations for the pod + podAnnotations: {} + # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment + podSecurityContext: + seccompProfile: + # -- Restrict a Container's Syscalls with seccomp + type: RuntimeDefault + # -- Runs all processes within a pod with a special uid + runAsUser: 10001 + # -- Processes within a pod will belong to this guid + runAsGroup: 10001 + # -- The owner for volumes and any files created within volumes will belong to this guid + fsGroup: 10001 + # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod + securityContext: + capabilities: + # -- Specifies which capabilities to drop to reduce syscall attack surface + drop: + - ALL + # -- Specifies which capabilities to add to issue specialized syscalls + add: [] + # -- Whether the root filesystem is mounted in read-only mode + readOnlyRootFilesystem: true + # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID + allowPrivilegeEscalation: false + # -- Requires the container to run without root privileges + runAsNonRoot: true + # -- The container's process will run with the specified uid + runAsUser: 10001 + # Extra environment variables that will be pass onto deployment pods + env: {} + # ENV_NAME: value + + # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # secretKeyRef: + # name: secret-name + # key: value_key + + # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from + envSecretNames: [] + # - first-secret + # - second-secret + + # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from + envConfigMapNames: [] + # - first-config-map + # - second-config-map + + ## Ingress declaration to expose the network service. + ingresses: + ## Public / Internet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.local" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - ids + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + ## Private / Intranet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.intranet" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - management + - control + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container + volumeMounts: [] + # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories + volumes: [] + # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container + resources: + {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + replicaCount: 1 + autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics + opentelemetry: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) + logging: |- + .level=INFO + org.eclipse.edc.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n + + # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes + nodeSelector: {} + # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes + tolerations: [] + # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on + affinity: {} + + url: + # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) + ids: "" + + dataplane: + image: + # -- Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically + repository: "" + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "" + initContainers: [] + debug: + enabled: false + port: 1044 + suspendOnStart: false + livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first liveness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + readinessProbe: + # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first readiness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + port: 80 + endpoints: + default: + port: 8080 + path: /api + public: + port: 8081 + path: /api/public + control: + port: 8083 + path: /api/dataplane/control + observability: + # -- port for incoming API calls + port: 8085 + # -- observability api, provides /health /readiness and /liveness endpoints + path: /observability + # -- allow or disallow insecure access, i.e. access without authentication + insecure: true + metrics: + port: 9090 + path: /metrics + aws: + endpointOverride: "" + accessKeyId: "" + secretAccessKey: "" + # -- additional labels for the pod + podLabels: {} + # -- additional annotations for the pod + podAnnotations: {} + # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment + podSecurityContext: + seccompProfile: + # -- Restrict a Container's Syscalls with seccomp + type: RuntimeDefault + # -- Runs all processes within a pod with a special uid + runAsUser: 10001 + # -- Processes within a pod will belong to this guid + runAsGroup: 10001 + # -- The owner for volumes and any files created within volumes will belong to this guid + fsGroup: 10001 + # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod + securityContext: + capabilities: + # -- Specifies which capabilities to drop to reduce syscall attack surface + drop: + - ALL + # -- Specifies which capabilities to add to issue specialized syscalls + add: [] + # -- Whether the root filesystem is mounted in read-only mode + readOnlyRootFilesystem: true + # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID + allowPrivilegeEscalation: false + # -- Requires the container to run without root privileges + runAsNonRoot: true + # -- The container's process will run with the specified uid + runAsUser: 10001 + # Extra environment variables that will be pass onto deployment pods + env: {} + # ENV_NAME: value + + # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # secretKeyRef: + # name: secret-name + # key: value_key + + # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from + envSecretNames: [] + # - first-secret + # - second-secret + + # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from + envConfigMapNames: [] + # - first-config-map + # - second-config-map + + ## Ingress declaration to expose the network service. + ingresses: + ## Public / Internet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-data.local" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - public + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container + volumeMounts: [] + # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories + volumes: [] + # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container + resources: + {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + replicaCount: 1 + autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics + opentelemetry: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) + logging: |- + .level=INFO + org.eclipse.edc.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n + # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes + nodeSelector: {} + # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes + tolerations: [] + # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on + affinity: {} + + url: + # -- Explicitly declared url for reaching the public api (e.g. if ingresses not used) + public: "" + + postgresql: + enabled: false + jdbcUrl: "" + username: "" + password: "" + + vault: + hashicorp: + enabled: true + url: "" + token: "" + timeout: 30 + healthCheck: + enabled: true + standbyOk: true + paths: + secret: /v1/secret + health: /v1/sys/health + secretNames: + transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key + transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key + transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key + dapsPrivateKey: daps-private-key + dapsPublicKey: daps-public-key + + daps: + url: "" + clientId: "" + paths: + jwks: /jwks.json + token: /token + + backendService: + httpProxyTokenReceiverUrl: "" + + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + imagePullSecrets: [] diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index 696e94396..8bac9b082 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -23,7 +23,11 @@ --- apiVersion: v2 name: tractusx-connector -description: A Helm chart for Tractus-X Eclipse Data Space Connector +description: | + A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a + Control Plane and a Data Plane. Note that _no_ external dependencies such as a PostgreSQL database and HashiCorp Vault are included. + + This chart is intended for use with an _existing_ PostgreSQL database and an _existing_ HashiCorp Vault. # A chart can be either an 'application' or a 'library' chart. # # Application charts are a collection of templates that can be packaged into versioned archives diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index 12c45b649..d4f89c56e 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -2,7 +2,10 @@ ![Version: 0.3.3](https://img.shields.io/badge/Version-0.3.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.3](https://img.shields.io/badge/AppVersion-0.3.3-informational?style=flat-square) -A Helm chart for Tractus-X Eclipse Data Space Connector +A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a +Control Plane and a Data Plane. Note that no external dependencies such as a PostgreSQL database and HashiCorp Vault are included. + +This chart is intended for use with an _existing_ PostgreSQL database and an _existing_ HashiCorp Vault. **Homepage:** @@ -121,6 +124,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 | controlplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | controlplane.tolerations | list | `[]` | | | controlplane.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | +| controlplane.url.readiness | string | `""` | | | controlplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | | controlplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | | customLabels | object | `{}` | | @@ -203,6 +207,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 | dataplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | dataplane.tolerations | list | `[]` | | | dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | +| dataplane.url.readiness | string | `""` | | | dataplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | | dataplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | | fullnameOverride | string | `""` | | @@ -216,13 +221,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 | serviceAccount.create | bool | `true` | | | serviceAccount.imagePullSecrets | list | `[]` | Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | | serviceAccount.name | string | `""` | | -| vault.azure.certificate | string | `nil` | | -| vault.azure.client | string | `""` | | -| vault.azure.enabled | bool | `false` | | -| vault.azure.name | string | `""` | | -| vault.azure.secret | string | `nil` | | -| vault.azure.tenant | string | `""` | | -| vault.hashicorp.enabled | bool | `false` | | +| vault.hashicorp.enabled | bool | `true` | | | vault.hashicorp.healthCheck.enabled | bool | `true` | | | vault.hashicorp.healthCheck.standbyOk | bool | `true` | | | vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index daab957e4..d529d8eae 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -59,18 +59,12 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.controlplane.securityContext | nindent 12 }} + + # either use the specified image, or use the default one {{- if .Values.controlplane.image.repository }} image: "{{ .Values.controlplane.image.repository }}:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" - {{- else if and .Values.postgresql.enabled .Values.vault.hashicorp.enabled }} - image: "tractusx/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" - {{- else if and .Values.postgresql.enabled .Values.vault.azure.enabled }} - image: "tractusx/edc-controlplane-postgresql:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" - {{- else if .Values.vault.hashicorp.enabled }} - image: "tractusx/edc-controlplane-memory-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" - {{- else if .Values.vault.azure.enabled }} - image: "tractusx/edc-runtime-memory:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- else }} - {{- fail "cannot choose control-plane image automatically based on configuration" }} + image: "tractusx/edc-controlplane-postgresql-hashicorp-vault:{{ .Values.controlplane.image.tag | default .Chart.AppVersion }}" {{- end }} imagePullPolicy: {{ .Values.controlplane.image.pullPolicy }} ports: @@ -186,8 +180,6 @@ spec: - name: "EDC_IDS_ENDPOINT_AUDIENCE" value: {{ printf "%s%s%s" (include "txdc.controlplane.url.protocol" . ) .Values.controlplane.endpoints.protocol.path "/data" | quote }} - {{- if .Values.postgresql.enabled }} - ################ ## POSTGRESQL ## ################ @@ -241,7 +233,6 @@ spec: value: {{ .Values.postgresql.password | required ".Values.postgresql.password is required" | quote }} - name: "EDC_DATASOURCE_TRANSFERPROCESS_URL" value: {{ .Values.postgresql.jdbcUrl | required ".Values.postgresql.jdbcUrl is required" | quote }} - {{- end }} ################ ## DATA PLANE ## @@ -275,7 +266,6 @@ spec: ## VAULT ## ########### - {{- if .Values.vault.hashicorp.enabled }} # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault - name: "EDC_VAULT_HASHICORP_URL" value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} @@ -291,29 +281,8 @@ spec: value: {{ .Values.vault.hashicorp.paths.secret | quote }} - name: "EDC_VAULT_HASHICORP_API_HEALTH_CHECK_PATH" value: {{ .Values.vault.hashicorp.paths.health | quote }} - {{- end }} - - {{- if .Values.vault.azure.enabled }} - - name: "EDC_VAULT_CLIENTID" - value: {{ .Values.vault.azure.client | required ".Values.vault.azure.client is required" | quote }} - - name: "EDC_VAULT_TENANTID" - value: {{ .Values.vault.azure.tenant | required ".Values.vault.azure.tenant is required" | quote }} - - name: "EDC_VAULT_NAME" - value: {{ .Values.vault.azure.name | required ".Values.vault.azure.name is required" | quote }} - # only set the env var if config value not null - {{- if .Values.vault.azure.secret }} - - name: "EDC_VAULT_CLIENTSECRET" - value: {{ .Values.vault.azure.secret | quote }} - {{- end }} - # only set the env var if config value not null - {{- if .Values.vault.azure.certificate }} - - name: "EDC_VAULT_CERTIFICATE" - value: {{ .Values.vault.azure.certificate | quote }} - {{- end }} - {{- end }} ##################### - ## DATA ENCRYPTION ## ##################### @@ -340,6 +309,8 @@ spec: ###################################### ## Additional environment variables ## ###################################### + - name: "EDC_CONNECTOR_NAME" + value: {{ include "txdc.fullname" .}}-controlplane {{- range $key, $value := .Values.controlplane.envValueFrom }} - name: {{ $key | quote }} valueFrom: diff --git a/charts/tractusx-connector/templates/deployment-dataplane.yaml b/charts/tractusx-connector/templates/deployment-dataplane.yaml index bbc48c434..c70cd2ff0 100644 --- a/charts/tractusx-connector/templates/deployment-dataplane.yaml +++ b/charts/tractusx-connector/templates/deployment-dataplane.yaml @@ -61,12 +61,8 @@ spec: {{- toYaml .Values.dataplane.securityContext | nindent 12 }} {{- if .Values.dataplane.image.repository }} image: "{{ .Values.dataplane.image.repository }}:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" - {{- else if and .Values.vault.hashicorp }} - image: "tractusx/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" - {{- else if .Values.vault.azure }} - image: "tractusx/edc-dataplane-azure-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- else }} - {{- fail "cannot choose data-plane image automatically based on configuration" }} + image: "tractusx/edc-dataplane-hashicorp-vault:{{ .Values.dataplane.image.tag | default .Chart.AppVersion }}" {{- end }} imagePullPolicy: {{ .Values.dataplane.image.pullPolicy }} ports: @@ -155,7 +151,6 @@ spec: ## VAULT ## ########### - {{- if .Values.vault.hashicorp.enabled }} # see extension https://github.com/eclipse-tractusx/tractusx-edc/tree/main/edc-extensions/hashicorp-vault - name: "EDC_VAULT_HASHICORP_URL" value: {{ .Values.vault.hashicorp.url | required ".Values.vault.hashicorp.url is required" | quote }} @@ -171,30 +166,12 @@ spec: value: {{ .Values.vault.hashicorp.paths.secret | quote }} - name: "EDC_VAULT_HASHICORP_API_HEALTH_CHECK_PATH" value: {{ .Values.vault.hashicorp.paths.health | quote }} - {{- end }} - - {{- if .Values.vault.azure.enabled }} - - name: "EDC_VAULT_CLIENTID" - value: {{ .Values.vault.azure.client | quote }} - - name: "EDC_VAULT_TENANTID" - value: {{ .Values.vault.azure.tenant | quote }} - - name: "EDC_VAULT_NAME" - value: {{ .Values.vault.azure.name | quote }} - # only set the env var if config value not null - {{- if .Values.vault.azure.secret }} - - name: "EDC_VAULT_CLIENTSECRET" - value: {{ .Values.vault.azure.secret | quote }} - {{- end }} - # only set the env var if config value not null - {{- if .Values.vault.azure.certificate }} - - name: "EDC_VAULT_CERTIFICATE" - value: {{ .Values.vault.azure.certificate | quote }} - {{- end }} - {{- end }} ###################################### ## Additional environment variables ## ###################################### + - name: "EDC_CONNECTOR_NAME" + value: {{ include "txdc.fullname" .}}-dataplane {{- range $key, $value := .Values.dataplane.envValueFrom }} - name: {{ $key | quote }} valueFrom: diff --git a/charts/tractusx-connector/templates/tests/test-controlplane-readiness.yaml b/charts/tractusx-connector/templates/tests/test-controlplane-readiness.yaml new file mode 100644 index 000000000..e2ed85c1f --- /dev/null +++ b/charts/tractusx-connector/templates/tests/test-controlplane-readiness.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{include "txdc.fullname" .}}test-controlplane-readiness" + labels: + {{- include "txdc.controlplane.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: curlimages/curl + command: ['curl'] + args: [ '{{- printf "http://%s-controlplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.controlplane.endpoints.observability.port $.Values.controlplane.endpoints.observability.path -}}' ] + restartPolicy: Never diff --git a/charts/tractusx-connector/templates/tests/test-dataplane-readiness.yaml b/charts/tractusx-connector/templates/tests/test-dataplane-readiness.yaml new file mode 100644 index 000000000..7533db1e3 --- /dev/null +++ b/charts/tractusx-connector/templates/tests/test-dataplane-readiness.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{include "txdc.fullname" .}}test-dataplane-readiness" + labels: + {{- include "txdc.dataplane.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: curlimages/curl + command: [ 'curl' ] + args: [ '{{- printf "http://%s-dataplane:%v%s/check/readiness" (include "txdc.fullname" $ ) $.Values.dataplane.endpoints.observability.port $.Values.dataplane.endpoints.observability.path -}}' ] + restartPolicy: Never diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index 21acfc20b..99fa498ab 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -38,7 +38,7 @@ controlplane: image: # -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically repository: "" - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion tag: "" @@ -291,7 +291,7 @@ dataplane: image: # -- Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically repository: "" - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use pullPolicy: IfNotPresent # -- Overrides the image tag whose default is the chart appVersion tag: "" @@ -495,7 +495,6 @@ postgresql: vault: hashicorp: - enabled: false url: "" token: "" timeout: 30 @@ -505,13 +504,6 @@ vault: paths: secret: /v1/secret health: /v1/sys/health - azure: - enabled: false - name: "" - client: "" - tenant: "" - secret: - certificate: secretNames: transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml index 6e7f24fe5..71e9bf3cf 100644 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml @@ -31,24 +31,3 @@ dependencies: repository: "file://../omejdn" alias: idsdaps condition: install.daps - - # HashiCorp Vault - - name: vault - alias: vault - version: 0.20.0 - repository: https://helm.releases.hashicorp.com - condition: install.vault - - # PostgreSQL - - name: postgresql - alias: postgresql - version: 12.1.6 - repository: https://charts.bitnami.com/bitnami - condition: install.postgresql - - # MinIo - - name: minio - alias: minio - repository: https://charts.min.io - version: 4.1.0 - condition: install.minio diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml index feba29cc4..b9d7d5faf 100644 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml @@ -5,10 +5,6 @@ ########### install: daps: true - postgresql: true - vault: true - minio: false - ######## # DAPS # @@ -47,139 +43,3 @@ idsdaps: UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= -----END CERTIFICATE----- -############## -# PostgreSQL # -############## -postgresql: - fullnameOverride: "postgresql" - primary: - persistence: - enabled: false - readReplicas: - persistence: - enabled: false - auth: - database: "edc" - username: "user" - password: "password" - -######### -# MINIO # -######### -minio: - fullnameOverride: minio - replicas: 2 - drivesPerNode: 0 - serviceAccount: - create: false - persistence: - size: 128Mi - resources: - requests: - memory: 128Mi - service: - type: NodePort - control: - port: 9000 - users: - - accessKey: qwerty123 - secretKey: qwerty123 - policy: customBucketPolicy - buckets: - # in some cases the minio API acts strange if there exists no bucket at all - - name: dummybucket - policy: none - purge: true - policies: - - name: customBucketPolicy - statements: - - resources: - - 'arn:aws:s3:::*' - actions: - - "s3:PutObject" - - "s3:ListBucket" - - "s3:CreateBucket" - - "s3:GetObject" - - "s3:DeleteObject" - - "s3:DeleteBucket" - -######### -# VAULT # -######### -vault: - fullnameOverride: "vault" - injector: - enabled: false - server: - dev: - enabled: true - devRootToken: "root" - # Must be the same certificate that is configured in section 'ids-daps' - postStart: - - "sh" - - "-c" - - | - { - - sleep 5 - - /bin/vault kv put secret/sokrates/data-encryption-aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== - - cat << EOF | /bin/vault kv put secret/sokrates/daps/daps-key content=- - -----BEGIN PRIVATE KEY----- - MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM - wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ - FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 - 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ - ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE - sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc - RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z - d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm - hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm - cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh - FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F - MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e - uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb - ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 - z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 - h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ - vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB - 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 - hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 - dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 - Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 - IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT - 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr - 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 - u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B - AjWFbUiBCFOo+gpAFcQGrkOQHA== - -----END PRIVATE KEY----- - EOF - - cat << EOF | /bin/vault kv put secret/sokrates/daps/daps-crt content=- - -----BEGIN CERTIFICATE----- - MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL - BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl - cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 - bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ - TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE - BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK - DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD - DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ - KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw - 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa - llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV - grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 - PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 - ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT - MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH - LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL - BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd - iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E - 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 - S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r - uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB - UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= - -----END CERTIFICATE----- - EOF - } From 6c98c4dadcfb456b750358a9992f0afb006c0fec Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" <100704677+tuncaytunc-zf@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:46:03 +0200 Subject: [PATCH 105/132] build(deps): Move Gradle dependencies constrains into root build.gradle.kts (#273) Co-authored-by: Florian Rusch (ZF Friedrichshafen AG) --- build.gradle.kts | 11 +++++++++++ .../edc-controlplane-postgresql/build.gradle.kts | 5 ----- .../edc-dataplane-azure-vault/build.gradle.kts | 5 ----- edc-extensions/control-plane-adapter/build.gradle.kts | 7 ------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1f545aea5..d3079176c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -159,3 +159,14 @@ subprojects { } } } + +dependencies { + constraints { + implementation("org.yaml:snakeyaml:2.0") { + because("version 1.33 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-1471.") + } + implementation("net.minidev:json-smart:2.4.10") { + because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") + } + } +} diff --git a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts index 78b5e253f..5888c34c4 100644 --- a/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts +++ b/edc-controlplane/edc-controlplane-postgresql/build.gradle.kts @@ -11,11 +11,6 @@ dependencies { runtimeOnly(project(":edc-controlplane:edc-controlplane-base")) runtimeOnly(project(":edc-extensions:postgresql-migration")) runtimeOnly(edc.azure.vault) - constraints { - implementation("net.minidev:json-smart:2.4.10") { - because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") - } - } runtimeOnly(edc.bundles.sqlstores) runtimeOnly(edc.transaction.local) runtimeOnly(edc.sql.pool) diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index 020dc0512..02d29b7db 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -8,11 +8,6 @@ plugins { dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) implementation(edc.azure.vault) - constraints { - implementation("net.minidev:json-smart:2.4.10") { - because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") - } - } implementation(edc.azure.identity) implementation("com.azure:azure-security-keyvault-secrets:4.6.0") } diff --git a/edc-extensions/control-plane-adapter/build.gradle.kts b/edc-extensions/control-plane-adapter/build.gradle.kts index c205d5cb0..fe34a0866 100644 --- a/edc-extensions/control-plane-adapter/build.gradle.kts +++ b/edc-extensions/control-plane-adapter/build.gradle.kts @@ -8,14 +8,7 @@ plugins { dependencies { implementation(edc.spi.core) implementation(edc.spi.policy) - implementation(edc.api.management) - constraints { - implementation("org.yaml:snakeyaml:2.0") { - because("version 1.33 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-1471.") - } - } - implementation(edc.spi.catalog) implementation(edc.spi.transactionspi) implementation(edc.spi.transaction.datasource) From 844b35cd8a17564ac17fbae44dd5f2b1aee9e5f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:31:16 +0200 Subject: [PATCH 106/132] chore(deps): bump com.azure:azure-security-keyvault-secrets from 4.6.0 to 4.6.1 (#272) * chore(deps): bump com.azure:azure-security-keyvault-secrets Bumps [com.azure:azure-security-keyvault-secrets](https://github.com/Azure/azure-sdk-for-java) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-cosmos_4.6.0...azure-messaging-eventgrid_4.6.1) --- updated-dependencies: - dependency-name: com.azure:azure-security-keyvault-secrets dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * trigger-ci --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Paul Latzelsperger --- edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts index 02d29b7db..906676e4f 100644 --- a/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts +++ b/edc-dataplane/edc-dataplane-azure-vault/build.gradle.kts @@ -9,7 +9,7 @@ dependencies { implementation(project(":edc-dataplane:edc-dataplane-base")) implementation(edc.azure.vault) implementation(edc.azure.identity) - implementation("com.azure:azure-security-keyvault-secrets:4.6.0") + implementation("com.azure:azure-security-keyvault-secrets:4.6.1") } tasks.withType { From 819f3b72adb00a7941667cd11f629667ba570849 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:31:28 +0200 Subject: [PATCH 107/132] chore(deps): bump actions/checkout from 3.3.0 to 3.5.2 (#254) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.3.0 to 3.5.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3.3.0...v3.5.2) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yaml | 6 +++--- .github/workflows/business-tests.yaml | 2 +- .github/workflows/deployment-test.yaml | 4 ++-- .github/workflows/draft-new-release.yaml | 2 +- .github/workflows/helm-chart-release.yaml | 2 +- .github/workflows/helm-lint.yaml | 2 +- .github/workflows/kics.yml | 2 +- .github/workflows/publish-docker.yaml | 2 +- .github/workflows/publish-new-release.yml | 8 ++++---- .github/workflows/trivy.yml | 4 ++-- .github/workflows/veracode.yaml | 4 ++-- .github/workflows/verify.yaml | 14 +++++++------- 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fd3fb7a93..9b14af068 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -71,7 +71,7 @@ jobs: needs: [ secret-presence ] steps: # Set-Up - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java # Build - name: Build Extensions @@ -99,7 +99,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/publish-docker-image name: Publish ${{ matrix.variant.img }} with: @@ -121,7 +121,7 @@ jobs: needs.secret-presence.outputs.GPG_PASSPHRASE && needs.secret-presence.outputs.GPG_PRIVATE_KEY && github.event_name != 'pull_request' && github.ref != 'refs/heads/releases' steps: # Set-Up - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java - name: Import GPG Key diff --git a/.github/workflows/business-tests.yaml b/.github/workflows/business-tests.yaml index cbf0f3767..5edc826cd 100644 --- a/.github/workflows/business-tests.yaml +++ b/.github/workflows/business-tests.yaml @@ -50,7 +50,7 @@ jobs: ### Set-Up ### ############## - - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java - diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index babb24a8e..1cde6d4f5 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest needs: test-prepare steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/run-deployment-test name: "Run deployment test using KinD and Helm" with: @@ -81,7 +81,7 @@ jobs: needs: test-prepare steps: - name: Checkout - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.2 - uses: ./.github/actions/run-deployment-test name: "Run deployment test using KinD and Helm" with: diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 98e3c956a..9ed3e9634 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -18,7 +18,7 @@ jobs: pages: write pull-requests: write steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - name: Create release branch run: git checkout -b release/${{ github.event.inputs.version }} diff --git a/.github/workflows/helm-chart-release.yaml b/.github/workflows/helm-chart-release.yaml index f19c841b9..40aeecc6d 100644 --- a/.github/workflows/helm-chart-release.yaml +++ b/.github/workflows/helm-chart-release.yaml @@ -38,7 +38,7 @@ jobs: steps: # fetch-depth: 0 is required to determine differences in chart(s) - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 with: fetch-depth: 0 diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml index 0b5a70f1f..79c691d98 100644 --- a/.github/workflows/helm-lint.yaml +++ b/.github/workflows/helm-lint.yaml @@ -26,7 +26,7 @@ jobs: ### Set-Up ### ############## - - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.2 with: fetch-depth: 0 - diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index 1b922064a..375b2d5be 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -20,7 +20,7 @@ jobs: security-events: write steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - name: KICS scan uses: checkmarx/kics-github-action@v1.5 diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index bbe7a5d10..f14d1e8fc 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -54,7 +54,7 @@ jobs: contents: write packages: write steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/publish-docker-image name: Publish ${{ matrix.variant.img }} with: diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 0da6f5da5..491af119b 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -54,7 +54,7 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java @@ -92,7 +92,7 @@ jobs: {dir: edc-dataplane, img: edc-dataplane-hashicorp-vault}] steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/publish-docker-image name: Publish ${{ matrix.variant.img }} with: @@ -119,7 +119,7 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.2 with: fetch-depth: 0 - @@ -166,7 +166,7 @@ jobs: run: | echo "RELEASE_VERSION=${{ needs.release-version.outputs.RELEASE_VERSION }}" >> $GITHUB_ENV - - uses: actions/checkout@v3.3.0 + uses: actions/checkout@v3.5.2 with: # 0 to fetch the full history due to upcoming merge of releases into main branch fetch-depth: 0 diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 2fe44c399..4a4520bb3 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -36,7 +36,7 @@ jobs: contents: read security-events: write steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - name: Run Trivy vulnerability scanner in repo mode uses: aquasecurity/trivy-action@master with: @@ -71,7 +71,7 @@ jobs: - edc-dataplane-azure-vault - edc-dataplane-hashicorp-vault steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 ## This step will fail if the docker images is not found - name: "Check if image exists" diff --git a/.github/workflows/veracode.yaml b/.github/workflows/veracode.yaml index b8900971c..83bcdfcd5 100644 --- a/.github/workflows/veracode.yaml +++ b/.github/workflows/veracode.yaml @@ -23,7 +23,7 @@ jobs: verify-formatting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 with: fetch-depth: 0 - uses: ./.github/actions/setup-java @@ -46,7 +46,7 @@ jobs: { dir: edc-dataplane, name: edc-dataplane-hashicorp-vault } ] steps: # Set-Up - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java # Build - name: Build ${{ matrix.variant.name }} diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index 2cd0432f8..f24fee8d4 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -58,7 +58,7 @@ jobs: verify-formatting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java - name: Verify proper formatting @@ -72,7 +72,7 @@ jobs: markdown-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - name: Install mardkdownlint run: npm install -g markdownlint-cli2 @@ -85,7 +85,7 @@ jobs: runs-on: ubuntu-latest needs: [ verify-formatting ] steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java @@ -96,7 +96,7 @@ jobs: runs-on: ubuntu-latest needs: [ verify-formatting ] steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java @@ -107,7 +107,7 @@ jobs: runs-on: ubuntu-latest needs: [ verify-formatting ] steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java @@ -118,7 +118,7 @@ jobs: runs-on: ubuntu-latest needs: [ verify-formatting ] steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 - uses: ./.github/actions/setup-java @@ -132,7 +132,7 @@ jobs: runs-on: ubuntu-latest steps: # Set-Up - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v3.5.2 with: fetch-depth: 0 - uses: ./.github/actions/setup-java From 37031c7e2d60e5bae61f8e88d7fc7fb78b09af30 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" <100704677+tuncaytunc-zf@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:58:18 +0200 Subject: [PATCH 108/132] build(deps): Move centralized dependency constrains to "allprojects" block within root build.gradle.kts (#274) --- build.gradle.kts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d3079176c..f0ca42f83 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,6 +56,15 @@ allprojects { // this is used to counter version conflicts between the JUnit version pulled in by the plugin, // and the one expected by IntelliJ testImplementation(platform("org.junit:junit-bom:5.9.2")) + + constraints { + implementation("org.yaml:snakeyaml:2.0") { + because("version 1.33 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-1471.") + } + implementation("net.minidev:json-smart:2.4.10") { + because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") + } + } } // configure which version of the annotation processor to use. defaults to the same version as the plugin @@ -159,14 +168,3 @@ subprojects { } } } - -dependencies { - constraints { - implementation("org.yaml:snakeyaml:2.0") { - because("version 1.33 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-1471.") - } - implementation("net.minidev:json-smart:2.4.10") { - because("version 2.4.8 has vulnerabilities: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-1370.") - } - } -} From 5eb0eb33b7c9340c62a7eeaa7bbb0b232cd8f834 Mon Sep 17 00:00:00 2001 From: Stephan Bauer <84396022+stephanbcbauer@users.noreply.github.com> Date: Tue, 25 Apr 2023 07:23:03 +0200 Subject: [PATCH 109/132] feat: delete add-to-project workflow (#276) This type of action doesn't work with the generated project. And there is a way to configure these kinds of workflows within the GitHub UI. --- .github/workflows/add-to-project.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/add-to-project.yml diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml deleted file mode 100644 index 00ffae871..000000000 --- a/.github/workflows/add-to-project.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Add to Project - -on: - issues: - types: [ opened ] - -jobs: - add-to-project: - runs-on: ubuntu-latest - steps: - - name: Add issue to project - uses: actions/add-to-project@v0.5.0 - with: - project-url: https://github.com/orgs/eclipse-tractusx/projects/5 - github-token: ${{ secrets.GITHUB_TOKEN }} - labeled: bug - label-operator: NOT From b15cb71e97f33dce70a513c1a56acffba7ea7f70 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Tue, 25 Apr 2023 15:04:55 +0200 Subject: [PATCH 110/132] Update DEPENDENCIES file --- DEPENDENCIES | 493 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 366 insertions(+), 127 deletions(-) diff --git a/DEPENDENCIES b/DEPENDENCIES index 3479de982..cf6b091ec 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,148 +1,387 @@ -maven/mavencentral/com.azure/azure-core-http-netty/1.12.7, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-core/1.34.0, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-identity/1.7.0, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.5.2, , restricted, clearlydefined -maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.14.0-rc2, Apache-2.0, approved, #5303 -maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.14.0-rc2, Apache-2.0 AND MIT, approved, #4303 -maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.0-rc2, Apache-2.0, approved, #4105 -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.14.0-rc2, Apache-2.0, restricted, clearlydefined -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.14.0-rc2, Apache-2.0, approved, #4300 -maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.14.0-rc2, Apache-2.0, approved, #4699 -maven/mavencentral/com.fasterxml.woodstox/woodstox-core/6.4.0, Apache-2.0, approved, #5309 +Invalid: com.azure:azure-identity:+, unknown, restricted, none +Invalid: net.minidev:json-smart:[1.3.3,2.4.8], unknown, restricted, none +maven/mavencentral/ch.randelshofer/fastdoubleparser/0.8.0, , restricted, clearlydefined +maven/mavencentral/com.azure/azure-core-http-netty/1.13.2, , restricted, clearlydefined +maven/mavencentral/com.azure/azure-core/1.38.0, , restricted, clearlydefined +maven/mavencentral/com.azure/azure-identity/1.6.0, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-json/1.0.0, , restricted, clearlydefined +maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.2.3, MIT, approved, clearlydefined +maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.6.1, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.10.3, Apache-2.0, approved, CQ21280 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.11.2, Apache-2.0, approved, CQ23491 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.13.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.13.5, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.14.2, Apache-2.0, approved, #5303 +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.0, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.13.3, Apache-2.0, approved, #2133 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.13.5, Apache-2.0, approved, #2133 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.14.2, Apache-2.0 AND MIT, approved, #4303 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.0, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.3, Apache-2.0, approved, #2134 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.4.2, Apache-2.0, approved, #2134 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.5, Apache-2.0, approved, #2134 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.2, Apache-2.0, approved, #4105 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.0, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.14.0, Apache-2.0, restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.14.2, Apache-2.0, restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.14.2, Apache-2.0, approved, #4300 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.13.3, Apache-2.0, approved, #2566 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.14.2, Apache-2.0, approved, #5933 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.13.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.13.5, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.14.2, Apache-2.0, approved, #4699 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.0, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-base/2.14.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.13.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.14.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.13.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.14.2, Apache-2.0, approved, #5308 +maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.14.2, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.15.0, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.woodstox/woodstox-core/6.5.0, Apache-2.0, restricted, clearlydefined +maven/mavencentral/com.fasterxml/classmate/1.5.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.github.docker-java/docker-java-api/3.3.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.github.docker-java/docker-java-transport-zerodep/3.3.0, , restricted, clearlydefined +maven/mavencentral/com.github.docker-java/docker-java-transport/3.3.0, , restricted, clearlydefined +maven/mavencentral/com.github.spotbugs/spotbugs-annotations/4.7.3, LGPL-2.1, restricted, clearlydefined maven/mavencentral/com.github.stephenc.jcip/jcip-annotations/1.0-1, Apache-2.0, approved, CQ21949 +maven/mavencentral/com.google.code.findbugs/jsr305/3.0.2, Apache-2.0, approved, #20 +maven/mavencentral/com.google.errorprone/error_prone_annotations/2.7.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.guava/failureaccess/1.0.1, Apache-2.0, approved, CQ22654 +maven/mavencentral/com.google.guava/guava/31.0.1-jre, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava, Apache-2.0, approved, CQ22657 +maven/mavencentral/com.google.j2objc/j2objc-annotations/1.3, Apache-2.0, approved, CQ21195 maven/mavencentral/com.microsoft.azure/msal4j-persistence-extension/1.1.0, MIT, approved, clearlydefined -maven/mavencentral/com.microsoft.azure/msal4j/1.13.3, MIT, approved, clearlydefined +maven/mavencentral/com.microsoft.azure/msal4j/1.13.7, MIT, approved, clearlydefined +maven/mavencentral/com.microsoft.azure/msal4j/1.4.0, MIT, approved, clearlydefined maven/mavencentral/com.nimbusds/content-type/2.2, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/lang-tag/1.6, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.nimbusds/nimbus-jose-jwt/8.23, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.22, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.25, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/oauth2-oidc-sdk/9.35, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.puppycrawl.tools/checkstyle/10.0, LGPL-2.1+, restricted, clearlydefined +maven/mavencentral/com.squareup.okhttp3/okhttp-dnsoverhttps/4.10.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.squareup.okhttp3/okhttp/4.10.0, Apache-2.0 AND MPL-2.0, approved, #3057 maven/mavencentral/com.squareup.okhttp3/okhttp/4.9.3, Apache-2.0 AND MPL-2.0, approved, #3225 -maven/mavencentral/com.squareup.okio/okio/2.8.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.squareup.okio/okio-jvm/3.0.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.squareup.okio/okio/3.0.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.sun.activation/jakarta.activation/2.0.1, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf +maven/mavencentral/commons-beanutils/commons-beanutils/1.9.4, Apache-2.0, approved, CQ12654 +maven/mavencentral/commons-codec/commons-codec/1.11, Apache-2.0 AND BSD-3-Clause, approved, CQ15971 +maven/mavencentral/commons-collections/commons-collections/3.2.2, Apache-2.0, approved, CQ10385 +maven/mavencentral/commons-logging/commons-logging/1.2, Apache-2.0, approved, CQ10162 maven/mavencentral/de.fraunhofer.iais.eis.ids.infomodel/java/4.1.3, Apache-2.0, approved, #3779 maven/mavencentral/de.fraunhofer.iais.eis.infomodel/util/4.1.3, Apache-2.0, approved, #3780 +maven/mavencentral/dev.failsafe/failsafe-okhttp/3.2.4, Apache-2.0, approved, clearlydefined maven/mavencentral/dev.failsafe/failsafe/3.2.4, Apache-2.0, approved, clearlydefined +maven/mavencentral/info.picocli/picocli/4.6.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.github.classgraph/classgraph/4.8.138, MIT, approved, CQ22530 maven/mavencentral/io.micrometer/micrometer-core/1.8.2, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.netty/netty-buffer/4.1.82.Final, Apache-2.0, approved, CQ21842 -maven/mavencentral/io.netty/netty-codec-dns/4.1.81.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-http/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-http2/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-socks/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-common/4.1.82.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 -maven/mavencentral/io.netty/netty-handler-proxy/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-handler/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.81.Final, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.81.Final, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.netty/netty-resolver-dns/4.1.81.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-resolver/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.54.Final, Apache-2.0 OR LicenseRef-Public-Domain OR BSD-2-Clause OR MIT, approved, CQ15280 -maven/mavencentral/io.netty/netty-tcnative-classes/2.0.54.Final, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.82.Final, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.82.Final, Apache-2.0, approved, #4107 -maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport/4.1.82.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.23, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.23, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.projectreactor/reactor-core/3.4.23, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-buffer/4.1.86.Final, Apache-2.0, approved, CQ21842 +maven/mavencentral/io.netty/netty-buffer/4.1.89.Final, Apache-2.0, approved, CQ21842 +maven/mavencentral/io.netty/netty-codec-dns/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http2/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http2/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-socks/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-common/4.1.86.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 +maven/mavencentral/io.netty/netty-common/4.1.89.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 +maven/mavencentral/io.netty/netty-handler-proxy/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.89.Final, Apache-2.0, approved, #6367 +maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.89.Final, Apache-2.0, approved, #7004 +maven/mavencentral/io.netty/netty-resolver-dns/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.56.Final, Apache-2.0 OR LicenseRef-Public-Domain OR BSD-2-Clause OR MIT, approved, CQ15280 +maven/mavencentral/io.netty/netty-tcnative-classes/2.0.56.Final, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.86.Final, Apache-2.0, approved, #6366 +maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.89.Final, Apache-2.0, approved, #6366 +maven/mavencentral/io.netty/netty-transport-classes-kqueue/4.1.89.Final, Apache-2.0, approved, #4107 +maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-kqueue/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport/4.1.86.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport/4.1.89.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.opentelemetry/opentelemetry-api/1.12.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.opentelemetry/opentelemetry-context/1.12.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.opentelemetry/opentelemetry-extension-annotations/1.12.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor.netty/reactor-netty-core/1.0.28, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor.netty/reactor-netty-http/1.0.28, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.projectreactor/reactor-core/3.4.27, Apache-2.0, approved, #7517 +maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.2, Apache-2.0, approved, #5947 +maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.2, Apache-2.0, approved, #5929 +maven/mavencentral/io.swagger.core.v3/swagger-integration-jakarta/2.2.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.swagger.core.v3/swagger-jaxrs2-jakarta/2.2.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.2, Apache-2.0, approved, #5919 +maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.0.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca +maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca +maven/mavencentral/jakarta.el/jakarta.el-api/4.0.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.el +maven/mavencentral/jakarta.inject/jakarta.inject-api/2.0.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.0, EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, #7697 +maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.0.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.rest maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.rest +maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/3.0.0, BSD-3-Clause, approved, ee4j.jaxb +maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/3.0.1, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/javax.validation/validation-api/2.0.1.Final, Apache-2.0, approved, CQ15302 +maven/mavencentral/junit/junit/4.13.2, EPL-2.0, approved, CQ23636 +maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.12.4, Apache-2.0, approved, #1810 +maven/mavencentral/net.bytebuddy/byte-buddy/1.12.4, Apache-2.0 AND BSD-3-Clause, approved, #1811 +maven/mavencentral/net.java.dev.jna/jna-platform/5.5.0, Apache-2.0 AND (Apache-2.0 AND LGPL-2.1-or-later), approved, #1514 maven/mavencentral/net.java.dev.jna/jna-platform/5.6.0, Apache-2.0 OR LGPL-2.1-or-later, approved, CQ22390 +maven/mavencentral/net.java.dev.jna/jna/5.12.1, Apache-2.0 OR LGPL-2.1-or-later, approved, #3217 maven/mavencentral/net.java.dev.jna/jna/5.5.0, Apache-2.0 or LGPL-2.1, approved, #1508 -maven/mavencentral/net.minidev/accessors-smart/2.4.7, Apache-2.0, approved, clearlydefined -maven/mavencentral/net.minidev/json-smart/2.4.7, Apache-2.0, approved, #3288 +maven/mavencentral/net.java.dev.jna/jna/5.6.0, Apache-2.0 AND LGPL-2.1-or-later, approved, CQ22391 +maven/mavencentral/net.minidev/accessors-smart/2.4.9, Apache-2.0, approved, #7515 +maven/mavencentral/net.minidev/json-smart/2.4.10, Apache-2.0, approved, #3288 +maven/mavencentral/net.sf.saxon/Saxon-HE/10.6, NOASSERTION, restricted, clearlydefined +maven/mavencentral/org.antlr/antlr4-runtime/4.9.3, BSD-3-Clause, approved, #322 +maven/mavencentral/org.apache.commons/commons-compress/1.22, Apache-2.0 AND BSD-3-Clause, approved, #4299 +maven/mavencentral/org.apache.commons/commons-lang3/3.12.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.commons/commons-pool2/2.11.1, Apache-2.0, approved, CQ23795 +maven/mavencentral/org.apache.commons/commons-text/1.10.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.httpcomponents/httpclient/4.5.13, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ23527 +maven/mavencentral/org.apache.httpcomponents/httpcore/4.4.13, Apache-2.0, approved, CQ23528 +maven/mavencentral/org.apache.sshd/sshd-common/2.9.2, Apache-2.0 AND ISC, approved, #3224 +maven/mavencentral/org.apache.sshd/sshd-core/2.9.2, Apache-2.0, approved, #3222 +maven/mavencentral/org.apache.sshd/sshd-sftp/2.9.2, Apache-2.0, approved, #6273 +maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.assertj/assertj-core/3.22.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.bouncycastle/bcpkix-jdk15on/1.70, MIT, approved, clearlydefined +maven/mavencentral/org.bouncycastle/bcpkix-jdk18on/1.72, MIT, approved, #3789 maven/mavencentral/org.bouncycastle/bcprov-jdk15on/1.70, MIT, approved, #1712 +maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.72, MIT AND CC0-1.0, approved, #3538 maven/mavencentral/org.bouncycastle/bcutil-jdk15on/1.70, MIT, approved, clearlydefined +maven/mavencentral/org.bouncycastle/bcutil-jdk18on/1.72, MIT, approved, #3790 +maven/mavencentral/org.checkerframework/checker-qual/3.12.0, MIT, approved, clearlydefined +maven/mavencentral/org.checkerframework/checker-qual/3.5.0, MIT, approved, clearlydefined maven/mavencentral/org.codehaus.woodstox/stax2-api/4.2.1, BSD-2-Clause, approved, #2670 -maven/mavencentral/org.eclipse.edc/aggregate-service-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/api-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/api-observability/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/asset-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/asset-index-sql/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/boot/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/catalog-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/catalog-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/configuration-filesystem/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/connector-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-agreement-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-definition-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-definition-store-sql/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-negotiation-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-negotiation-store-sql/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/contract-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-api-client-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/control-plane-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/core-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-aws-s3/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-framework/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-http/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-selector-client/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-selector-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-selector-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-transfer-client/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-transfer-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/data-plane-transfer-sync/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/http-receiver/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/http/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/ids-api-configuration/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/ids-api-multipart-dispatcher-v1/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/ids-api-multipart-endpoint-v1/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/ids-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/ids-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/ids-transform-v1/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jersey-micrometer/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jetty-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jetty-micrometer/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/jwt-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/management-api-configuration/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/management-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/micrometer-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/monitor-jdk-logger/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-daps/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/oauth2-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-definition-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-definition-store-sql/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-model/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/policy-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.0.1-20221208.055231-48, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-core/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/sql-pool-apache-commons/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-local/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transaction-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-process-api/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-process-store-sql/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transfer-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/transform-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/util/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/vault-azure/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.edc/web-spi/0.0.1-20221201-20221201.072646-1, Apache-2.0, approved, technology.edc -maven/mavencentral/org.eclipse.tractusx.edc.extensions/business-partner-validation/0.1.4-SNAPSHOT, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.eclipse.tractusx.edc.extensions/cx-oauth2/0.1.4-SNAPSHOT, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.eclipse.tractusx.edc.extensions/data-encryption/0.1.4-SNAPSHOT, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.eclipse.tractusx.edc.extensions/dataplane-selector-configuration/0.1.4-SNAPSHOT, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.eclipse.tractusx.edc.extensions/hashicorp-vault/0.1.4-SNAPSHOT, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.eclipse.tractusx.edc.extensions/postgresql-migration/0.1.4-SNAPSHOT, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.eclipse.tractusx.edc/edc-controlplane-base/0.1.4-SNAPSHOT, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.eclipse.tractusx.edc/edc-dataplane-base/0.1.4-SNAPSHOT, Apache-2.0, approved, automotive.tractusx -maven/mavencentral/org.flywaydb/flyway-core/9.8.3, , restricted, clearlydefined +maven/mavencentral/org.eclipse.edc/aggregate-service-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/api-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/asset-index-sql/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/auth-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/auth-tokenbased/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/autodoc-processor/0.0.1-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/aws-s3-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/boot/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/catalog-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/catalog-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/configuration-filesystem/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/connector-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-agreement-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-definition-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-definition-store-sql/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-negotiation-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-negotiation-store-sql/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/contract-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-api-configuration/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-aggregate-services/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-api-client-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/control-plane-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/core-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-aws-s3/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-client/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-framework/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-oauth2/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-http/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-selector-client/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-selector-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-selector-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/data-plane-util/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/http/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ids-api-configuration/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ids-api-multipart-dispatcher-v1/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ids-api-multipart-endpoint-v1/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ids-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ids-jsonld-serdes/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ids-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ids-transform-v1/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/ids/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jersey-micrometer/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jetty-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jetty-micrometer/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/junit/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jwt-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/jwt-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/management-api-configuration/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/management-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/micrometer-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/oauth2-client/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/oauth2-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/oauth2-daps/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/oauth2-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-definition-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-definition-store-sql/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-engine/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-evaluator/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-model/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/policy-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.0.1-20230220-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.0.1-SNAPSHOT, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-lease/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/sql-pool-apache-commons/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/state-machine/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-local/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transaction-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-core/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-data-plane-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-data-plane/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-process-api/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-process-store-sql/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-pull-http-dynamic-receiver/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transfer-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/transform-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/util/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/vault-azure/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.edc/web-spi/0.0.1-20230220.patch1, Apache-2.0, approved, technology.edc +maven/mavencentral/org.eclipse.jetty.toolchain/jetty-jakarta-servlet-api/5.0.2, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.toolchain/jetty-jakarta-websocket-api/2.0.0, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-client/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-common/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-core-server/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-client/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-common/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-jakarta-server/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-servlet/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-alpn-client/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-annotations/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-client/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-http/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-io/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-jndi/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-plus/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-security/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-server/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.flywaydb/flyway-core/9.16.3, , restricted, clearlydefined +maven/mavencentral/org.glassfish.hk2.external/aopalliance-repackaged/3.0.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish +maven/mavencentral/org.glassfish.hk2/hk2-api/3.0.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish +maven/mavencentral/org.glassfish.hk2/hk2-locator/3.0.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish +maven/mavencentral/org.glassfish.hk2/hk2-utils/3.0.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish +maven/mavencentral/org.glassfish.hk2/osgi-resource-locator/1.0.3, CDDL-1.0, approved, CQ10889 +maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet-core/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.containers/jersey-container-servlet/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-client/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-common/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.core/jersey-server/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.ext/jersey-bean-validation/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.ext/jersey-entity-filtering/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.inject/jersey-hk2/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.media/jersey-media-json-jackson/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey +maven/mavencentral/org.glassfish/jakarta.el/4.0.2, NOASSERTION, restricted, clearlydefined +maven/mavencentral/org.hamcrest/hamcrest-core/1.3, BSD-2-Clause, approved, CQ11429 maven/mavencentral/org.hdrhistogram/HdrHistogram/2.1.12, , approved, CQ13192 -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.4.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.hibernate.validator/hibernate-validator/7.0.1.Final, Apache-2.0, approved, CQ22746 +maven/mavencentral/org.jacoco/org.jacoco.agent/0.8.8, EPL-2.0, approved, CQ23285 +maven/mavencentral/org.jacoco/org.jacoco.ant/0.8.8, EPL-2.0, approved, #1068 +maven/mavencentral/org.jacoco/org.jacoco.core/0.8.8, EPL-2.0, approved, CQ23283 +maven/mavencentral/org.jacoco/org.jacoco.report/0.8.8, EPL-2.0 AND Apache-2.0, approved, CQ23284 +maven/mavencentral/org.javassist/javassist/3.25.0-GA, MPL-1.1 OR LGPL-2.1-or-later OR Apache-2.0, approved, CQ19885 +maven/mavencentral/org.javassist/javassist/3.28.0-GA, Apache-2.0 OR LGPL-2.1-or-later OR MPL-1.1, approved, #327 +maven/mavencentral/org.jboss.logging/jboss-logging/3.4.1.Final, Apache-2.0, approved, CQ21255 +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.5.31, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.6.20, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.6.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.5.31, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.6.10, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.6.20, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains/annotations/13.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jetbrains/annotations/15.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.ow2.asm/asm/9.1, BSD-3-Clause, approved, CQ23029 -maven/mavencentral/org.ow2.asm/asm/9.3, BSD-3-Clause, approved, CQ24052 -maven/mavencentral/org.postgresql/postgresql/42.5.1, BSD-2-Clause, approved, #3416 -maven/mavencentral/org.projectlombok/lombok/1.18.24, MIT AND LicenseRef-Public-Domain, approved, CQ23907 +maven/mavencentral/org.jetbrains/annotations/17.0.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.jetbrains/annotations/24.0.1, Apache-2.0, approved, #7417 +maven/mavencentral/org.junit-pioneer/junit-pioneer/2.0.0, EPL-2.0, approved, clearlydefined +maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.8.2, EPL-2.0, approved, #1291 +maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.9.0, EPL-2.0, approved, #3133 +maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.9.2, EPL-2.0, approved, #3133 +maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.8.2, EPL-2.0, approved, #1292 +maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.9.0, EPL-2.0, approved, #3125 +maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.9.2, EPL-2.0, approved, #3125 +maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.8.2, EPL-2.0, approved, #1488 +maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.9.0, EPL-2.0, approved, #3134 +maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.9.2, EPL-2.0, approved, #3134 +maven/mavencentral/org.junit.platform/junit-platform-commons/1.9.0, EPL-2.0, approved, #3130 +maven/mavencentral/org.junit.platform/junit-platform-commons/1.9.2, EPL-2.0, approved, #3130 +maven/mavencentral/org.junit.platform/junit-platform-engine/1.9.0, EPL-2.0, approved, #3128 +maven/mavencentral/org.junit.platform/junit-platform-engine/1.9.2, EPL-2.0, approved, #3128 +maven/mavencentral/org.junit.platform/junit-platform-launcher/1.9.0, EPL-2.0, approved, #3132 +maven/mavencentral/org.junit/junit-bom/5.9.0, EPL-2.0, approved, #4711 +maven/mavencentral/org.junit/junit-bom/5.9.2, EPL-2.0, approved, #4711 +maven/mavencentral/org.jvnet.mimepull/mimepull/1.9.13, CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, CQ21484 +maven/mavencentral/org.latencyutils/LatencyUtils/2.0.3, BSD-2-Clause, approved, CQ17408 +maven/mavencentral/org.mockito/mockito-core/4.2.0, MIT, approved, clearlydefined +maven/mavencentral/org.objenesis/objenesis/3.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.opentest4j/opentest4j/1.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-analysis/9.2, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-analysis/9.3, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-commons/9.2, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-commons/9.3, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-tree/9.2, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-tree/9.3, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm/9.2, BSD-3-Clause, approved, CQ23635 +maven/mavencentral/org.ow2.asm/asm/9.3, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.postgresql/postgresql/42.4.0, BSD-2-Clause, approved, #3112 +maven/mavencentral/org.projectlombok/lombok/1.18.26, MIT AND LicenseRef-Public-Domain, approved, CQ23907 +maven/mavencentral/org.reactivestreams/reactive-streams/1.0.3, CC0-1.0, approved, CQ16332 maven/mavencentral/org.reactivestreams/reactive-streams/1.0.4, CC0-1.0, approved, CQ16332 -maven/mavencentral/org.slf4j/slf4j-api/2.0.3, MIT, approved, CQ24285 +maven/mavencentral/org.reflections/reflections/0.10.2, Apache-2.0 AND WTFPL, approved, clearlydefined +maven/mavencentral/org.rnorth.duct-tape/duct-tape/1.0.8, MIT, approved, clearlydefined +maven/mavencentral/org.slf4j/jcl-over-slf4j/1.7.32, Apache-2.0, approved, CQ12843 +maven/mavencentral/org.slf4j/slf4j-api/1.7.25, MIT, approved, CQ13368 +maven/mavencentral/org.slf4j/slf4j-api/1.7.30, MIT, approved, CQ13368 +maven/mavencentral/org.slf4j/slf4j-api/1.7.32, MIT, approved, CQ13368 +maven/mavencentral/org.slf4j/slf4j-api/1.7.35, MIT, approved, CQ13368 +maven/mavencentral/org.slf4j/slf4j-api/1.7.36, MIT, approved, CQ13368 +maven/mavencentral/org.slf4j/slf4j-api/1.7.7, MIT, approved, CQ9827 +maven/mavencentral/org.slf4j/slf4j-api/2.0.0, MIT, approved, #5915 +maven/mavencentral/org.slf4j/slf4j-api/2.0.0-alpha7, MIT, approved, #5915 +maven/mavencentral/org.slf4j/slf4j-api/2.0.7, MIT, approved, #5915 +maven/mavencentral/org.testcontainers/junit-jupiter/1.18.0, , restricted, clearlydefined +maven/mavencentral/org.testcontainers/testcontainers/1.18.0, , restricted, clearlydefined +maven/mavencentral/org.testcontainers/vault/1.18.0, , restricted, clearlydefined +maven/mavencentral/org.yaml/snakeyaml/1.33, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.yaml/snakeyaml/2.0, Apache-2.0 AND (Apache-2.0 OR BSD-3-Clause OR EPL-1.0 OR GPL-2.0-or-later OR LGPL-2.1-or-later), approved, #7275 +maven/mavencentral/software.amazon.awssdk/annotations/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/apache-client/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/arns/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/auth/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-core/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-query-protocol/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/aws-xml-protocol/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/endpoints-spi/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/http-client-spi/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/iam/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/json-utils/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/metrics-spi/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/netty-nio-client/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/profiles/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/protocol-core/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/regions/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/s3/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/sdk-core/2.19.26, Apache-2.0, restricted, clearlydefined +maven/mavencentral/software.amazon.awssdk/sts/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.19.26, Apache-2.0, restricted, clearlydefined +maven/mavencentral/software.amazon.awssdk/utils/2.19.26, Apache-2.0, approved, clearlydefined +maven/mavencentral/software.amazon.eventstream/eventstream/1.0.1, Apache-2.0, approved, clearlydefined From e659059331a752a63870d0cd67a35c5b41fc8a34 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Tue, 25 Apr 2023 17:21:00 +0200 Subject: [PATCH 111/132] Add license and copyright header to the charts --- .../templates/_helpers.tpl | 22 ++++++++++ .../templates/configmap-runtime.yaml | 40 +++++++++---------- .../templates/deployment-runtime.yaml | 40 +++++++++---------- .../templates/hpa-runtime.yaml | 22 ++++++++++ .../templates/ingress-runtime.yaml | 24 ++++++++++- .../templates/service-runtime.yaml | 40 +++++++++---------- .../templates/serviceaccount.yaml | 22 ++++++++++ .../tractusx-connector/templates/_helpers.tpl | 22 ++++++++++ .../templates/configmap-dataplane.yaml | 22 ++++++++++ .../templates/hpa-controlplane.yaml | 22 ++++++++++ .../templates/hpa-dataplane.yaml | 22 ++++++++++ .../templates/ingress-controlplane.yaml | 22 ++++++++++ .../templates/ingress-dataplane.yaml | 22 ++++++++++ .../templates/service-dataplane.yaml | 22 ++++++++++ .../templates/serviceaccount.yaml | 22 ++++++++++ 15 files changed, 325 insertions(+), 61 deletions(-) diff --git a/charts/tractusx-connector-memory/templates/_helpers.tpl b/charts/tractusx-connector-memory/templates/_helpers.tpl index 1b70bf13b..6c457c413 100644 --- a/charts/tractusx-connector-memory/templates/_helpers.tpl +++ b/charts/tractusx-connector-memory/templates/_helpers.tpl @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{/* Expand the name of the chart. */}} diff --git a/charts/tractusx-connector-memory/templates/configmap-runtime.yaml b/charts/tractusx-connector-memory/templates/configmap-runtime.yaml index 8b6067e06..eddd69c55 100644 --- a/charts/tractusx-connector-memory/templates/configmap-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/configmap-runtime.yaml @@ -1,24 +1,24 @@ # - # Copyright (c) 2023 ZF Friedrichshafen AG - # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH - # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation - # - # See the NOTICE file(s) distributed with this work for additional - # information regarding copyright ownership. - # - # 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 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - # License for the specific language governing permissions and limitations - # under the License. - # - # SPDX-License-Identifier: Apache-2.0 - # +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# --- apiVersion: v1 diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml index 82d162ad8..fcbccfe6f 100644 --- a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -1,24 +1,24 @@ # - # Copyright (c) 2023 ZF Friedrichshafen AG - # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH - # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation - # - # See the NOTICE file(s) distributed with this work for additional - # information regarding copyright ownership. - # - # 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 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - # License for the specific language governing permissions and limitations - # under the License. - # - # SPDX-License-Identifier: Apache-2.0 - # +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# --- apiVersion: apps/v1 diff --git a/charts/tractusx-connector-memory/templates/hpa-runtime.yaml b/charts/tractusx-connector-memory/templates/hpa-runtime.yaml index a373dfb63..2cce3c6f2 100644 --- a/charts/tractusx-connector-memory/templates/hpa-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/hpa-runtime.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{- if .Values.runtime.autoscaling.enabled }} --- apiVersion: autoscaling/v2beta1 diff --git a/charts/tractusx-connector-memory/templates/ingress-runtime.yaml b/charts/tractusx-connector-memory/templates/ingress-runtime.yaml index 06c6f5c68..7a514b147 100644 --- a/charts/tractusx-connector-memory/templates/ingress-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/ingress-runtime.yaml @@ -1,4 +1,26 @@ -{{- $fullName := include "txdc.fullname" . }} +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + + {{- $fullName := include "txdc.fullname" . }} {{- $controlLabels := include "txdc.runtime.labels" . | nindent 4 }} {{- $controlEdcEndpoints := .Values.runtime.endpoints }} {{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} diff --git a/charts/tractusx-connector-memory/templates/service-runtime.yaml b/charts/tractusx-connector-memory/templates/service-runtime.yaml index 241e28885..77b3edb41 100644 --- a/charts/tractusx-connector-memory/templates/service-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/service-runtime.yaml @@ -1,24 +1,24 @@ # - # Copyright (c) 2023 ZF Friedrichshafen AG - # Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH - # Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - # Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation - # - # See the NOTICE file(s) distributed with this work for additional - # information regarding copyright ownership. - # - # 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 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - # License for the specific language governing permissions and limitations - # under the License. - # - # SPDX-License-Identifier: Apache-2.0 - # +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# --- apiVersion: v1 diff --git a/charts/tractusx-connector-memory/templates/serviceaccount.yaml b/charts/tractusx-connector-memory/templates/serviceaccount.yaml index c650bcd68..401b2dec0 100644 --- a/charts/tractusx-connector-memory/templates/serviceaccount.yaml +++ b/charts/tractusx-connector-memory/templates/serviceaccount.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount diff --git a/charts/tractusx-connector/templates/_helpers.tpl b/charts/tractusx-connector/templates/_helpers.tpl index 701e6fc75..3a02f6ca4 100644 --- a/charts/tractusx-connector/templates/_helpers.tpl +++ b/charts/tractusx-connector/templates/_helpers.tpl @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{/* Expand the name of the chart. */}} diff --git a/charts/tractusx-connector/templates/configmap-dataplane.yaml b/charts/tractusx-connector/templates/configmap-dataplane.yaml index 4f4c1a456..87fd401c3 100644 --- a/charts/tractusx-connector/templates/configmap-dataplane.yaml +++ b/charts/tractusx-connector/templates/configmap-dataplane.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + --- apiVersion: v1 kind: ConfigMap diff --git a/charts/tractusx-connector/templates/hpa-controlplane.yaml b/charts/tractusx-connector/templates/hpa-controlplane.yaml index 36fe8fae0..c52ed9152 100644 --- a/charts/tractusx-connector/templates/hpa-controlplane.yaml +++ b/charts/tractusx-connector/templates/hpa-controlplane.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{- if .Values.controlplane.autoscaling.enabled }} --- apiVersion: autoscaling/v2beta1 diff --git a/charts/tractusx-connector/templates/hpa-dataplane.yaml b/charts/tractusx-connector/templates/hpa-dataplane.yaml index abad34fcc..519c0e526 100644 --- a/charts/tractusx-connector/templates/hpa-dataplane.yaml +++ b/charts/tractusx-connector/templates/hpa-dataplane.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{- if .Values.controlplane.autoscaling.enabled }} --- apiVersion: autoscaling/v2beta1 diff --git a/charts/tractusx-connector/templates/ingress-controlplane.yaml b/charts/tractusx-connector/templates/ingress-controlplane.yaml index a2325b17c..ee490510f 100644 --- a/charts/tractusx-connector/templates/ingress-controlplane.yaml +++ b/charts/tractusx-connector/templates/ingress-controlplane.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{- $fullName := include "txdc.fullname" . }} {{- $controlLabels := include "txdc.controlplane.labels" . | nindent 4 }} {{- $controlEdcEndpoints := .Values.controlplane.endpoints }} diff --git a/charts/tractusx-connector/templates/ingress-dataplane.yaml b/charts/tractusx-connector/templates/ingress-dataplane.yaml index 1f79d09c1..14e88bff9 100644 --- a/charts/tractusx-connector/templates/ingress-dataplane.yaml +++ b/charts/tractusx-connector/templates/ingress-dataplane.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{- $fullName := include "txdc.fullname" . }} {{- $dataLabels := include "txdc.dataplane.labels" . | nindent 4 }} {{- $dataEdcEndpoints := .Values.dataplane.endpoints }} diff --git a/charts/tractusx-connector/templates/service-dataplane.yaml b/charts/tractusx-connector/templates/service-dataplane.yaml index 5644f7fbe..07ee9cba3 100644 --- a/charts/tractusx-connector/templates/service-dataplane.yaml +++ b/charts/tractusx-connector/templates/service-dataplane.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + --- apiVersion: v1 kind: Service diff --git a/charts/tractusx-connector/templates/serviceaccount.yaml b/charts/tractusx-connector/templates/serviceaccount.yaml index c650bcd68..401b2dec0 100644 --- a/charts/tractusx-connector/templates/serviceaccount.yaml +++ b/charts/tractusx-connector/templates/serviceaccount.yaml @@ -1,3 +1,25 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + {{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount From 5efa765c7d328af6f02da8d9bb6f57d711ce8f0b Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Tue, 25 Apr 2023 17:56:34 +0200 Subject: [PATCH 112/132] fix chart typo --- charts/tractusx-connector-memory/templates/ingress-runtime.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/tractusx-connector-memory/templates/ingress-runtime.yaml b/charts/tractusx-connector-memory/templates/ingress-runtime.yaml index 7a514b147..a5209ebbf 100644 --- a/charts/tractusx-connector-memory/templates/ingress-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/ingress-runtime.yaml @@ -20,7 +20,7 @@ # SPDX-License-Identifier: Apache-2.0 # - {{- $fullName := include "txdc.fullname" . }} +{{- $fullName := include "txdc.fullname" . }} {{- $controlLabels := include "txdc.runtime.labels" . | nindent 4 }} {{- $controlEdcEndpoints := .Values.runtime.endpoints }} {{- $gitVersion := .Capabilities.KubeVersion.GitVersion }} From f5d6917f4523db3344a8bdaec003cb0b6b480f13 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Tue, 25 Apr 2023 18:06:25 +0200 Subject: [PATCH 113/132] fix charts --- .../templates/_helpers.tpl | 22 ------------------- .../tractusx-connector/templates/_helpers.tpl | 22 ------------------- 2 files changed, 44 deletions(-) diff --git a/charts/tractusx-connector-memory/templates/_helpers.tpl b/charts/tractusx-connector-memory/templates/_helpers.tpl index 6c457c413..1b70bf13b 100644 --- a/charts/tractusx-connector-memory/templates/_helpers.tpl +++ b/charts/tractusx-connector-memory/templates/_helpers.tpl @@ -1,25 +1,3 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - {{/* Expand the name of the chart. */}} diff --git a/charts/tractusx-connector/templates/_helpers.tpl b/charts/tractusx-connector/templates/_helpers.tpl index 3a02f6ca4..701e6fc75 100644 --- a/charts/tractusx-connector/templates/_helpers.tpl +++ b/charts/tractusx-connector/templates/_helpers.tpl @@ -1,25 +1,3 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - {{/* Expand the name of the chart. */}} From 7e7fa6bae6d0383d0b5ea676e18c830d4d1f0662 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Wed, 26 Apr 2023 09:22:13 +0200 Subject: [PATCH 114/132] Update DEPENDENCIES file --- DEPENDENCIES | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/DEPENDENCIES b/DEPENDENCIES index cf6b091ec..06cf6a2e9 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,48 +1,48 @@ Invalid: com.azure:azure-identity:+, unknown, restricted, none Invalid: net.minidev:json-smart:[1.3.3,2.4.8], unknown, restricted, none -maven/mavencentral/ch.randelshofer/fastdoubleparser/0.8.0, , restricted, clearlydefined -maven/mavencentral/com.azure/azure-core-http-netty/1.13.2, , restricted, clearlydefined -maven/mavencentral/com.azure/azure-core/1.38.0, , restricted, clearlydefined +maven/mavencentral/ch.randelshofer/fastdoubleparser/0.8.0, MIT AND (BSD-2-Clause AND MIT), approved, #7926 +maven/mavencentral/com.azure/azure-core-http-netty/1.13.2, MIT AND Apache-2.0, approved, #7948 +maven/mavencentral/com.azure/azure-core/1.38.0, MIT AND Apache-2.0, approved, #7939 maven/mavencentral/com.azure/azure-identity/1.6.0, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-json/1.0.0, , restricted, clearlydefined +maven/mavencentral/com.azure/azure-json/1.0.0, MIT AND Apache-2.0, approved, #7933 maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.2.3, MIT, approved, clearlydefined -maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.6.1, , restricted, clearlydefined +maven/mavencentral/com.azure/azure-security-keyvault-secrets/4.6.1, MIT, approved, #7940 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.10.3, Apache-2.0, approved, CQ21280 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.11.2, Apache-2.0, approved, CQ23491 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.13.3, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.13.5, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.14.2, Apache-2.0, approved, #5303 -maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.0, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.0, Apache-2.0, approved, #7947 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.13.3, Apache-2.0, approved, #2133 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.13.5, Apache-2.0, approved, #2133 maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.14.2, Apache-2.0 AND MIT, approved, #4303 -maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.0, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.0, MIT AND Apache-2.0, approved, #7932 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.3, Apache-2.0, approved, #2134 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.4.2, Apache-2.0, approved, #2134 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.5, Apache-2.0, approved, #2134 maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.14.2, Apache-2.0, approved, #4105 -maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.0, , restricted, clearlydefined -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.14.0, Apache-2.0, restricted, clearlydefined -maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.14.2, Apache-2.0, restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.0, Apache-2.0, approved, #7934 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.14.0, Apache-2.0 AND BSD-3-Clause AND MIT AND Apache-2.0, approved, #7943 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-toml/2.14.2, Apache-2.0 AND BSD-3-Clause AND MIT AND Apache-2.0, approved, #7944 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.14.2, Apache-2.0, approved, #4300 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.13.3, Apache-2.0, approved, #2566 maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.14.2, Apache-2.0, approved, #5933 maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.13.3, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.13.5, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.14.2, Apache-2.0, approved, #4699 -maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.0, , restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.0, None, restricted, #7930 maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-base/2.14.2, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.13.3, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.jakarta.rs/jackson-jakarta-rs-json-provider/2.14.2, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.13.3, Apache-2.0, approved, clearlydefined maven/mavencentral/com.fasterxml.jackson.module/jackson-module-jakarta-xmlbind-annotations/2.14.2, Apache-2.0, approved, #5308 -maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.14.2, , restricted, clearlydefined -maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.15.0, , restricted, clearlydefined -maven/mavencentral/com.fasterxml.woodstox/woodstox-core/6.5.0, Apache-2.0, restricted, clearlydefined +maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.14.2, Apache-2.0, approved, #7931 +maven/mavencentral/com.fasterxml.jackson/jackson-bom/2.15.0, Apache-2.0, approved, #7929 +maven/mavencentral/com.fasterxml.woodstox/woodstox-core/6.5.0, Apache-2.0, approved, #7950 maven/mavencentral/com.fasterxml/classmate/1.5.1, Apache-2.0, approved, clearlydefined maven/mavencentral/com.github.docker-java/docker-java-api/3.3.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.github.docker-java/docker-java-transport-zerodep/3.3.0, , restricted, clearlydefined -maven/mavencentral/com.github.docker-java/docker-java-transport/3.3.0, , restricted, clearlydefined +maven/mavencentral/com.github.docker-java/docker-java-transport-zerodep/3.3.0, Apache-2.0 AND (Apache-2.0 AND BSD-3-Clause), approved, #7946 +maven/mavencentral/com.github.docker-java/docker-java-transport/3.3.0, Apache-2.0, approved, #7942 maven/mavencentral/com.github.spotbugs/spotbugs-annotations/4.7.3, LGPL-2.1, restricted, clearlydefined maven/mavencentral/com.github.stephenc.jcip/jcip-annotations/1.0-1, Apache-2.0, approved, CQ21949 maven/mavencentral/com.google.code.findbugs/jsr305/3.0.2, Apache-2.0, approved, #20 @@ -59,7 +59,7 @@ maven/mavencentral/com.nimbusds/lang-tag/1.6, Apache-2.0, approved, clearlydefin maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.22, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.25, Apache-2.0, approved, clearlydefined maven/mavencentral/com.nimbusds/oauth2-oidc-sdk/9.35, Apache-2.0, approved, clearlydefined -maven/mavencentral/com.puppycrawl.tools/checkstyle/10.0, LGPL-2.1+, restricted, clearlydefined +maven/mavencentral/com.puppycrawl.tools/checkstyle/10.0, LGPL-2.1-or-later AND (Apache-2.0 AND LGPL-2.1-or-later) AND (LGPL-2.1-or-later AND LicenseRef-scancode-proprietary-license) AND Apache-2.0, restricted, #7936 maven/mavencentral/com.squareup.okhttp3/okhttp-dnsoverhttps/4.10.0, Apache-2.0, approved, clearlydefined maven/mavencentral/com.squareup.okhttp3/okhttp/4.10.0, Apache-2.0 AND MPL-2.0, approved, #3057 maven/mavencentral/com.squareup.okhttp3/okhttp/4.9.3, Apache-2.0 AND MPL-2.0, approved, #3225 @@ -139,7 +139,7 @@ maven/mavencentral/net.java.dev.jna/jna/5.5.0, Apache-2.0 or LGPL-2.1, approved, maven/mavencentral/net.java.dev.jna/jna/5.6.0, Apache-2.0 AND LGPL-2.1-or-later, approved, CQ22391 maven/mavencentral/net.minidev/accessors-smart/2.4.9, Apache-2.0, approved, #7515 maven/mavencentral/net.minidev/json-smart/2.4.10, Apache-2.0, approved, #3288 -maven/mavencentral/net.sf.saxon/Saxon-HE/10.6, NOASSERTION, restricted, clearlydefined +maven/mavencentral/net.sf.saxon/Saxon-HE/10.6, LicenseRef-scancode-iso-8879 AND (W3C AND W3C-19980720), restricted, #7945 maven/mavencentral/org.antlr/antlr4-runtime/4.9.3, BSD-3-Clause, approved, #322 maven/mavencentral/org.apache.commons/commons-compress/1.22, Apache-2.0 AND BSD-3-Clause, approved, #4299 maven/mavencentral/org.apache.commons/commons-lang3/3.12.0, Apache-2.0, approved, clearlydefined @@ -274,7 +274,7 @@ maven/mavencentral/org.eclipse.jetty/jetty-servlet/11.0.12, EPL-2.0 OR Apache-2. maven/mavencentral/org.eclipse.jetty/jetty-util/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-webapp/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty maven/mavencentral/org.eclipse.jetty/jetty-xml/11.0.12, EPL-2.0 OR Apache-2.0, approved, rt.jetty -maven/mavencentral/org.flywaydb/flyway-core/9.16.3, , restricted, clearlydefined +maven/mavencentral/org.flywaydb/flyway-core/9.16.3, Apache-2.0, approved, #7935 maven/mavencentral/org.glassfish.hk2.external/aopalliance-repackaged/3.0.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish maven/mavencentral/org.glassfish.hk2/hk2-api/3.0.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish maven/mavencentral/org.glassfish.hk2/hk2-locator/3.0.3, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.glassfish @@ -290,7 +290,7 @@ maven/mavencentral/org.glassfish.jersey.ext/jersey-entity-filtering/3.0.8, EPL-2 maven/mavencentral/org.glassfish.jersey.inject/jersey-hk2/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey maven/mavencentral/org.glassfish.jersey.media/jersey-media-json-jackson/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey maven/mavencentral/org.glassfish.jersey.media/jersey-media-multipart/3.0.8, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey -maven/mavencentral/org.glassfish/jakarta.el/4.0.2, NOASSERTION, restricted, clearlydefined +maven/mavencentral/org.glassfish/jakarta.el/4.0.2, EPL-2.0 AND (GPL-2.0-only WITH Classpath-exception-2.0) AND (EPL-2.0 AND (GPL-2.0-only WITH Classpath-exception-2.0) AND LicenseRef-scancode-generic-export-compliance), restricted, #7937 maven/mavencentral/org.hamcrest/hamcrest-core/1.3, BSD-2-Clause, approved, CQ11429 maven/mavencentral/org.hdrhistogram/HdrHistogram/2.1.12, , approved, CQ13192 maven/mavencentral/org.hibernate.validator/hibernate-validator/7.0.1.Final, Apache-2.0, approved, CQ22746 @@ -358,9 +358,9 @@ maven/mavencentral/org.slf4j/slf4j-api/1.7.7, MIT, approved, CQ9827 maven/mavencentral/org.slf4j/slf4j-api/2.0.0, MIT, approved, #5915 maven/mavencentral/org.slf4j/slf4j-api/2.0.0-alpha7, MIT, approved, #5915 maven/mavencentral/org.slf4j/slf4j-api/2.0.7, MIT, approved, #5915 -maven/mavencentral/org.testcontainers/junit-jupiter/1.18.0, , restricted, clearlydefined -maven/mavencentral/org.testcontainers/testcontainers/1.18.0, , restricted, clearlydefined -maven/mavencentral/org.testcontainers/vault/1.18.0, , restricted, clearlydefined +maven/mavencentral/org.testcontainers/junit-jupiter/1.18.0, None, restricted, #7941 +maven/mavencentral/org.testcontainers/testcontainers/1.18.0, None, restricted, #7938 +maven/mavencentral/org.testcontainers/vault/1.18.0, None, restricted, #7927 maven/mavencentral/org.yaml/snakeyaml/1.33, Apache-2.0, approved, clearlydefined maven/mavencentral/org.yaml/snakeyaml/2.0, Apache-2.0 AND (Apache-2.0 OR BSD-3-Clause OR EPL-1.0 OR GPL-2.0-or-later OR LGPL-2.1-or-later), approved, #7275 maven/mavencentral/software.amazon.awssdk/annotations/2.19.26, Apache-2.0, approved, clearlydefined @@ -380,8 +380,8 @@ maven/mavencentral/software.amazon.awssdk/profiles/2.19.26, Apache-2.0, approved maven/mavencentral/software.amazon.awssdk/protocol-core/2.19.26, Apache-2.0, approved, clearlydefined maven/mavencentral/software.amazon.awssdk/regions/2.19.26, Apache-2.0, approved, clearlydefined maven/mavencentral/software.amazon.awssdk/s3/2.19.26, Apache-2.0, approved, clearlydefined -maven/mavencentral/software.amazon.awssdk/sdk-core/2.19.26, Apache-2.0, restricted, clearlydefined +maven/mavencentral/software.amazon.awssdk/sdk-core/2.19.26, Apache-2.0, approved, #7928 maven/mavencentral/software.amazon.awssdk/sts/2.19.26, Apache-2.0, approved, clearlydefined -maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.19.26, Apache-2.0, restricted, clearlydefined +maven/mavencentral/software.amazon.awssdk/third-party-jackson-core/2.19.26, Apache-2.0, approved, #7949 maven/mavencentral/software.amazon.awssdk/utils/2.19.26, Apache-2.0, approved, clearlydefined maven/mavencentral/software.amazon.eventstream/eventstream/1.0.1, Apache-2.0, approved, clearlydefined From 60d8faad5eed64d9512faf84e71f685b61e46b6a Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Wed, 26 Apr 2023 10:14:14 +0200 Subject: [PATCH 115/132] Fix charts --- .../templates/tests/test-readiness.yaml | 23 +++++++++++++++++++ .../templates/serviceaccount.yaml | 1 + 2 files changed, 24 insertions(+) diff --git a/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml index d98493953..497e619a1 100644 --- a/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml +++ b/charts/tractusx-connector-memory/templates/tests/test-readiness.yaml @@ -1,3 +1,26 @@ +# +# Copyright (c) 2023 ZF Friedrichshafen AG +# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- apiVersion: v1 kind: Pod metadata: diff --git a/charts/tractusx-connector/templates/serviceaccount.yaml b/charts/tractusx-connector/templates/serviceaccount.yaml index 401b2dec0..d432ecdf7 100644 --- a/charts/tractusx-connector/templates/serviceaccount.yaml +++ b/charts/tractusx-connector/templates/serviceaccount.yaml @@ -20,6 +20,7 @@ # SPDX-License-Identifier: Apache-2.0 # +--- {{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount From d91d31301ee9d1108f9e96ac8f75ed1e4b37a6b5 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Wed, 26 Apr 2023 10:33:29 +0200 Subject: [PATCH 116/132] Fix charts --- charts/tractusx-connector-memory/templates/serviceaccount.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/tractusx-connector-memory/templates/serviceaccount.yaml b/charts/tractusx-connector-memory/templates/serviceaccount.yaml index 401b2dec0..d432ecdf7 100644 --- a/charts/tractusx-connector-memory/templates/serviceaccount.yaml +++ b/charts/tractusx-connector-memory/templates/serviceaccount.yaml @@ -20,6 +20,7 @@ # SPDX-License-Identifier: Apache-2.0 # +--- {{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount From 80b70e614642f15ed526073959e412c2d21e3568 Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Wed, 26 Apr 2023 10:42:35 +0200 Subject: [PATCH 117/132] Fix charts --- charts/tractusx-connector-memory/templates/serviceaccount.yaml | 2 +- charts/tractusx-connector/templates/serviceaccount.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/tractusx-connector-memory/templates/serviceaccount.yaml b/charts/tractusx-connector-memory/templates/serviceaccount.yaml index d432ecdf7..4a6e1ac07 100644 --- a/charts/tractusx-connector-memory/templates/serviceaccount.yaml +++ b/charts/tractusx-connector-memory/templates/serviceaccount.yaml @@ -20,8 +20,8 @@ # SPDX-License-Identifier: Apache-2.0 # ---- {{- if .Values.serviceAccount.create -}} +--- apiVersion: v1 kind: ServiceAccount metadata: diff --git a/charts/tractusx-connector/templates/serviceaccount.yaml b/charts/tractusx-connector/templates/serviceaccount.yaml index d432ecdf7..4a6e1ac07 100644 --- a/charts/tractusx-connector/templates/serviceaccount.yaml +++ b/charts/tractusx-connector/templates/serviceaccount.yaml @@ -20,8 +20,8 @@ # SPDX-License-Identifier: Apache-2.0 # ---- {{- if .Values.serviceAccount.create -}} +--- apiVersion: v1 kind: ServiceAccount metadata: From e545d92dac0e86cb6a5a76a585ad03d2d014ef8e Mon Sep 17 00:00:00 2001 From: "Tuncay Tunc (ZF Friedrichshafen AG)" Date: Wed, 26 Apr 2023 15:41:25 +0200 Subject: [PATCH 118/132] Create new connector certificates --- .../supporting-infrastructure/values.yaml | 236 +++++++++--------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml index 07c1e0b3b..26684294f 100644 --- a/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml +++ b/edc-tests/cucumber/src/main/resources/deployment/helm/supporting-infrastructure/values.yaml @@ -23,28 +23,28 @@ idsdaps: # Must be the same certificate that is stores in section 'sokrates-vault' certificate: |- -----BEGIN CERTIFICATE----- - MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + MIIEAzCCAuugAwIBAgIULy0aTdGiGkyvVp7l5Ccoq7DQREgwDQYJKoZIhvcNAQEL BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ - TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + TjEyMzQwHhcNMjMwNDI2MTI1OTE5WhcNMzMwNDIzMTI1OTE5WjCBkDELMAkGA1UE BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ - KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw - 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa - llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV - grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 - PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 - ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT - MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH - LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL - BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd - iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E - 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 - S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r - uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB - UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + KoZIhvcNAQEBBQADggEPADCCAQoCggEBALCr0h3vT5kNnwWhAmGRvEEo38nKyiXB + Gx0GlepYToKklMgtGIX25OkOrXJqq4BzybxN27DoWvU9DEixylkCbhwmwmpI3IhF + 8w6cV/odaYdQ3tEeZ6zWYXqKx+MVWTHQ8A4Njy64PWNDWBZmaGvxeE48i7EJnnrM + M5CGDAKbA/Jd1nlFxaq9hGiaCHa2kCNKdfrJ6ZUda5rPlLJk5at3VPxvRIpT50Gp + 3P4PtdwpwIHwa7y1xTBc43bEfcD1lmR9VkkxCX8lg4V1OBLx1GVwoUZBkN8P4POT + t+gQq7FbDbBEeOSmKELC3Tc8D8JCGv94sEg6o+4yzgpvyIvMyV8uGcECAwEAAaNT + MFEwHQYDVR0OBBYEFIxEsuJTl+5V8vTCUhMGhWsmdQShMB8GA1UdIwQYMBaAFIxE + suJTl+5V8vTCUhMGhWsmdQShMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAJHOKmFNZDk5xebzBARgcIrYrmRb5pIU4gNCWh/q1TF0+CFnnK8RTFTZ + 12pTbid6v5knn/f9bsilnudhxBzBQ4bukiI8Be0nzYfZU2dTU+w1cl/JnJfkGirt + 8Nwqv3fiUXfFBl8nE0RduAk9XF/UBIZXPapE6u1zR29jvuV+ppmhQrFFeJufeBGd + Wwn6XGK4fzENGDyjdk4QB/dg3/heM5h330vIGO4hVvlQBfJhNbC7Iikkr5ulytfd + deuZIfa7hG6WgIgGhg3YL1p/TTpJamBDS860PWyI7RH3o53VPphu/y2Rpud5AECV + xcrqaSGUTZPVyTUB8BxE32LqFDbpZb4= -----END CERTIFICATE----- - id: 99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23:keyid:99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23 name: plato @@ -53,28 +53,28 @@ idsdaps: # Must be the same certificate that is stores in section 'plato-vault' certificate: |- -----BEGIN CERTIFICATE----- - MIID7TCCAtWgAwIBAgIUJ0bwxUc7n3YK89mOyGXrLx2KO0YwDQYJKoZIhvcNAQEL + MIID7TCCAtWgAwIBAgIUJv9K1yHIGf/crrkLHtKlYUd06OwwDQYJKoZIhvcNAQEL BQAwgYUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 - bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRjLmRlbW8uY2F0ZW5hLXgubmV0MB4XDTIy - MDQyNTEwMjgwMloXDTIzMDQyNTEwMjgwMlowgYUxCzAJBgNVBAYTAkRFMQ8wDQYD + bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRjLmRlbW8uY2F0ZW5hLXgubmV0MB4XDTIz + MDQyNjEyNTQxNFoXDTMzMDQyMzEyNTQxNFowgYUxCzAJBgNVBAYTAkRFMQ8wDQYD VQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEMMAoGA1UECgwDQk1XMSAwHgYD VQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRj LmRlbW8uY2F0ZW5hLXgubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAm1/UvHXuRU1peEGHULZBP8j8gorXAQvUz8Znb1iVNtldI29GCSXkTHxph66x - TcegdF8aeaoU1mPf3LzMQUU1koZHUq6sRC+50uFcZJ2AjF5IXKQlDPNWR5tPXP56 - RZyqXxjPFHeuTA+YsYyrzEVhzEieOiNaxJDM3uV5pv+FTRHz+xMOgNBonR1QyMh6 - tcwB+EQagoeFl0DjEXAel9WG4hOG/rDiXArTMaVjnTG/ycmF0HeSnbRC+3/+fh/C - hzQJyEbviX67ymyYRJTyynt/Mtrqg5/ssdISexjw3ZmiFNemZIOhIdepoSwnJHFM - 4Jj8B5lq0a1jY5Rc9lDj710RXQIDAQABo1MwUTAdBgNVHQ4EFgQUmYOnF4b/mJPO - oN2h8Tb69g91CiMwHwYDVR0jBBgwFoAUmYOnF4b/mJPOoN2h8Tb69g91CiMwDwYD - VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAKxv/MTIEKNkzReOqrzpt - LM00X6JsDdfxa3rZ0Uq17PjO0R63IPsqzexhfZUML0e/Dwpe97xpvftCOEuICMBA - wOHhQc77MgwyF4dqgRgfJysxw37ACZxU6GI/K2JpKXQLgEhP14oHUIWOzCAbgDhR - jwOx3ZP176Vjxx90pW3hOphRVnq/BRqqEDtFwRzKtGnGvP8ecmC2iY4dXEA3QEp1 - gzg03eglvZSoedEPY5o5y/4n6TplaDmaeoo0QrvAiWik1gY85Lg21aBWVsP45wVS - tFn3m1FCCV8XYIj/EEUAh8VEhphLVEViE6m9Mm4deFDavXcGBb63BCiOQtnjd3eY - zQ== + AQEAwajlhy7Uf6V5P7UNmb5fSL0uJLt1EIeqEuLMTc8J6VhAgv7WGtRJiZySIFfQ + VBcwizO0C+pzyHXY957HJKFWhuiiACmO1NBOQuF5TCe/X9MHUOfK1l52mDu6zXhV + pPRn8ZY7CBul/94Wb/SS0+SJ6ogkdB8nwI++3ET166Wuv1aSdGKAc2daseQ1Ynau + fPL2ziVQDfPh51+9VUG8tuwGrwagFrawDk5FkZB7Z+nPZ0WpmLN2Q+oVRFaSgDRu + cTx6ejMs2tMSKzJIJjIVzgHRIULyPSMS3AlwHub3FwZp2TrXolczDJWL+XIbfHfw + 6KKNWGR80/RM457AhNzApd0GYQIDAQABo1MwUTAdBgNVHQ4EFgQU4TU1BUk881qd + H+g1I2jAuL+jAyAwHwYDVR0jBBgwFoAU4TU1BUk881qdH+g1I2jAuL+jAyAwDwYD + VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEOkReVM7644reZoPIWEP + 4XVyXKwoIk/zxVPedtcCoXWdG2zVXULvjIc0vf65Ih1e9b65rz+v6yn0isZ5zfSm + mFOsVMg0vL9I4QQtOSx9tL5MXq+zPeRLVpeRvvUD67+wKkf9n+e1DByqCVfaF68U + DDq0L0VQgp3fRNEzLjcXlOOIQ4W/qc1lnxoxVCzQuLJwkZejokV9cj5JDBojmAuK + g+IDL1aArzzKMD5iAAqm0rDbDnMhn0Km+AshDEWgAqtnsVEBRlt+GDAc+d0nplLY + BR8UsaLdtAVHaivXCaZGpjiOsvdOpTwWOaU9HOkuf/1uX5DTaxtClt2BXAnxF3Ug + iQ== -----END CERTIFICATE----- ############## @@ -172,59 +172,59 @@ vault: cat << EOF | /bin/vault kv put secret/plato/daps/my-plato-daps-key content=- -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCbX9S8de5FTWl4 - QYdQtkE/yPyCitcBC9TPxmdvWJU22V0jb0YJJeRMfGmHrrFNx6B0Xxp5qhTWY9/c - vMxBRTWShkdSrqxEL7nS4VxknYCMXkhcpCUM81ZHm09c/npFnKpfGM8Ud65MD5ix - jKvMRWHMSJ46I1rEkMze5Xmm/4VNEfP7Ew6A0GidHVDIyHq1zAH4RBqCh4WXQOMR - cB6X1YbiE4b+sOJcCtMxpWOdMb/JyYXQd5KdtEL7f/5+H8KHNAnIRu+JfrvKbJhE - lPLKe38y2uqDn+yx0hJ7GPDdmaIU16Zkg6Eh16mhLCckcUzgmPwHmWrRrWNjlFz2 - UOPvXRFdAgMBAAECggEAN2yd5IRk9I/CucUWUfJRoEE/4glI3PSte1iY+R0uTRyI - nuVIpGbB447VzjLAyLAXSqvKM/A58qg56PHoIrhffd8sfhAVH1WvAcymOrX8bxYK - 1hEvrkj3VB/Q1alpUH+sPrQI2pI+uJ8vptY5SmrNkiOtXavS6x+EFVbiaHHpyS26 - ASaCoRpdBoNTm0SAiDBTK6MqTs4vRpqKseGdC76F+jKimYrTJY19ZctSIAMjrnqd - qzRL+jfob5vMqKC22AjInkZ8BZWll1ZoTnv37bq2NAb9lvdY73REm42Wpm5S7PET - Eixe69gvi/IwaSe27S36+kcrQoYHnxbb31+Xt+0pQQKBgQDJfA2ZnYmcA3yvVQhi - e76I3rq6AEfcG4EDhf+JRO2QHKMMXLwfFAdSR8QflxNUWy1y6q/783EpgLJ1Kv8h - uNkTH6JyV7kFhwfvxWreAWx2jRQRACqnuaLnJ/28vd8Il0kc3/BQsWzbg6YTERrq - 0Au2RW/c9blrKS0MyurtOtZsiQKBgQDFaezSCWUspeNci5lrdvMiHBLOUgR2guQm - Gtf9RdBmzvtBqpdkP8AEMhRW7oSGcKpDldd0Klyml7s/CDYTL7sflHtKRiTQmWuJ - +p3uvyylAxr/Swfw56hj5Y4/Oj2CLIuUlglewo40JnvvM5icT7RGvbyaIIhYzIsR - HTv3t8eRNQKBgA4l8eaJk3IrJIRDWlVgDx8ZVM9e2azxGXwf2rPO7UejWyexE1yz - UVhLxc/aEfdod6aMKFNu4tFhQibMICJEEqovHH8e/dUPiFUj7b8tJmqkuXYAJv6k - IHZO7phkVNcLmIy4hO2Fp/k6I11PZC588XWZJqPDdYO63nj5fsmtygTRAoGBAJ72 - YH/wmMuO+Ll4n51tNvJscKg6WuWjGFumme2T3fArEx8ZYraSruex+7bUcVpgNnod - mlQsGFb9LwXecsyYTrFrOqvgN5zRLUr5x1qMDkMBcSfJHyfZIjruidBX8Vd0zyBi - gEERoLhVlM5UWbrkY2HjPo9NSv1WF1U8mSErl0NRAoGAYC3RxEfGxD9+Qi08nQgg - s/48hLdD2k2q4t3FrDsIGPAIEs52CGp9JWil9RyIQxBXWejETwDz+PgmD6U86Mhh - Qf5css6pcP/w1XF8vsyXfPnecgPSyOE4CgLtnQLxNriMiy5pfALELLyxoBQ+nquz - fMNLPC4K85ps/Uu9uzSatl0= + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDBqOWHLtR/pXk/ + tQ2Zvl9IvS4ku3UQh6oS4sxNzwnpWECC/tYa1EmJnJIgV9BUFzCLM7QL6nPIddj3 + nsckoVaG6KIAKY7U0E5C4XlMJ79f0wdQ58rWXnaYO7rNeFWk9GfxljsIG6X/3hZv + 9JLT5InqiCR0HyfAj77cRPXrpa6/VpJ0YoBzZ1qx5DVidq588vbOJVAN8+HnX71V + Qby27AavBqAWtrAOTkWRkHtn6c9nRamYs3ZD6hVEVpKANG5xPHp6Myza0xIrMkgm + MhXOAdEhQvI9IxLcCXAe5vcXBmnZOteiVzMMlYv5cht8d/Dooo1YZHzT9EzjnsCE + 3MCl3QZhAgMBAAECggEBAKR/RphRWwciE5/dtrPFVUKAD1X8NS/ZTMnGBCyDlLO0 + 1vduZ4dakyxk5mq6rKcBG6biQClu+PJpx+Zt5FJlCQ6HRDRHGKAEYLXGuDXL/W7z + 3d8HRPBaRPqCoeYuNPFs+W3oYjQ86AAzMXPfl2iNU+j3w58vZ6DVeRW5LfsAPTMg + Z8Sooa1jD2a/7uDN4lC0FGkTWif//Dio5tbijqeG8xqBnS8iKi4hgxcQA9azd0KB + 6uwvbX/izq4sVR2ZjxtT9WPX1cpOcXjUZBM9px9eAwLPmsM/AUAOHkKkd1DPYLjX + yyB0qvz+LmUQdJv11yGagsW7lrrvsBsro4ZMp0Ot1wECgYEA5j2XFCKNUcX/8OFm + 8E9q6DXyrd9T3rMxPYWR9nRwV0upN9Zd9mnvOKnl5MYQSgP0XJgwwyHawmG3wIcx + 0puf3uWi2lSpt6aafMCW6JEJbK/49XSPAjrptwkZUcCT3XJv1tMZuXzhv/p4t24o + hw9/EtzVxK7thGGZD6sDsQtbOlkCgYEA11OUofuD1VWN5YwFciPv0RhyfDyYYK7e + nPMXEoiBMQJnGkp3eaUzUgej/V93VtJcg9h0Tqn6NpI4rWUfUdi5ihZ6+hcvUIO4 + Roh+Oxpmu0yBfuBo7Uwf5XMpoQu74Z+cr24Pv32YtEUshUZidMuvOMaBXNJGlKiG + DjbCUV0CG0kCgYAVHvlJA5JrOfqsokDLMr3f53MHuED9YPrXZfVp4myb1XkEgknE + XRtw20UXo4PDBnHYPK3ceLKUuloc80oCw/v6ep5h4PpguovZfeFaHFP9AHeaLMMh + tT3TaKZF9aCa4/CWiG8HsQkUj2mbiiN1oFpL5K5HiLSJPFrKMSn5h80qoQKBgQC2 + obt1UEDXFwONaJ/N2dE0RkoEOdj8WBWUhVJSc9kv2lvcnsCLOqU2tChRZUFxMGcr + pNGxTtZcptTPrO9NmkZ0avDPYg7NeYs4t9hpBNGRlyhWlrwoWOLM2Eq8v5kRmzFo + Ui+lOT/l1q4WNEaZzZDG1Qcv1WHsAKwDLkrOe9ankQKBgQDM1fdqraKN2lCkDSPU + /Uw5nmFA7gNJQ9ta6CVITlDMWFb+e2OcDK7pKT1iEhJAfGndtQ0lwK6I5VDDxhXY + DGcU2UIWMAiJOZILDVjkny9brrIQ/fTwZps2qWNJ0bmYsmwPCe9QskNWz8sYAY6p + eBB+WUqNNqBb25p2CmcwqoT7Tg== -----END PRIVATE KEY----- EOF cat << EOF | /bin/vault kv put secret/plato/daps/my-plato-daps-crt content=- -----BEGIN CERTIFICATE----- - MIID7TCCAtWgAwIBAgIUJ0bwxUc7n3YK89mOyGXrLx2KO0YwDQYJKoZIhvcNAQEL + MIID7TCCAtWgAwIBAgIUJv9K1yHIGf/crrkLHtKlYUd06OwwDQYJKoZIhvcNAQEL BQAwgYUxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 - bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRjLmRlbW8uY2F0ZW5hLXgubmV0MB4XDTIy - MDQyNTEwMjgwMloXDTIzMDQyNTEwMjgwMlowgYUxCzAJBgNVBAYTAkRFMQ8wDQYD + bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRjLmRlbW8uY2F0ZW5hLXgubmV0MB4XDTIz + MDQyNjEyNTQxNFoXDTMzMDQyMzEyNTQxNFowgYUxCzAJBgNVBAYTAkRFMQ8wDQYD VQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEMMAoGA1UECgwDQk1XMSAwHgYD VQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0bmVyMjEkMCIGA1UEAwwbcGxhdG8tZWRj LmRlbW8uY2F0ZW5hLXgubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC - AQEAm1/UvHXuRU1peEGHULZBP8j8gorXAQvUz8Znb1iVNtldI29GCSXkTHxph66x - TcegdF8aeaoU1mPf3LzMQUU1koZHUq6sRC+50uFcZJ2AjF5IXKQlDPNWR5tPXP56 - RZyqXxjPFHeuTA+YsYyrzEVhzEieOiNaxJDM3uV5pv+FTRHz+xMOgNBonR1QyMh6 - tcwB+EQagoeFl0DjEXAel9WG4hOG/rDiXArTMaVjnTG/ycmF0HeSnbRC+3/+fh/C - hzQJyEbviX67ymyYRJTyynt/Mtrqg5/ssdISexjw3ZmiFNemZIOhIdepoSwnJHFM - 4Jj8B5lq0a1jY5Rc9lDj710RXQIDAQABo1MwUTAdBgNVHQ4EFgQUmYOnF4b/mJPO - oN2h8Tb69g91CiMwHwYDVR0jBBgwFoAUmYOnF4b/mJPOoN2h8Tb69g91CiMwDwYD - VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAKxv/MTIEKNkzReOqrzpt - LM00X6JsDdfxa3rZ0Uq17PjO0R63IPsqzexhfZUML0e/Dwpe97xpvftCOEuICMBA - wOHhQc77MgwyF4dqgRgfJysxw37ACZxU6GI/K2JpKXQLgEhP14oHUIWOzCAbgDhR - jwOx3ZP176Vjxx90pW3hOphRVnq/BRqqEDtFwRzKtGnGvP8ecmC2iY4dXEA3QEp1 - gzg03eglvZSoedEPY5o5y/4n6TplaDmaeoo0QrvAiWik1gY85Lg21aBWVsP45wVS - tFn3m1FCCV8XYIj/EEUAh8VEhphLVEViE6m9Mm4deFDavXcGBb63BCiOQtnjd3eY - zQ== + AQEAwajlhy7Uf6V5P7UNmb5fSL0uJLt1EIeqEuLMTc8J6VhAgv7WGtRJiZySIFfQ + VBcwizO0C+pzyHXY957HJKFWhuiiACmO1NBOQuF5TCe/X9MHUOfK1l52mDu6zXhV + pPRn8ZY7CBul/94Wb/SS0+SJ6ogkdB8nwI++3ET166Wuv1aSdGKAc2daseQ1Ynau + fPL2ziVQDfPh51+9VUG8tuwGrwagFrawDk5FkZB7Z+nPZ0WpmLN2Q+oVRFaSgDRu + cTx6ejMs2tMSKzJIJjIVzgHRIULyPSMS3AlwHub3FwZp2TrXolczDJWL+XIbfHfw + 6KKNWGR80/RM457AhNzApd0GYQIDAQABo1MwUTAdBgNVHQ4EFgQU4TU1BUk881qd + H+g1I2jAuL+jAyAwHwYDVR0jBBgwFoAU4TU1BUk881qdH+g1I2jAuL+jAyAwDwYD + VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEOkReVM7644reZoPIWEP + 4XVyXKwoIk/zxVPedtcCoXWdG2zVXULvjIc0vf65Ih1e9b65rz+v6yn0isZ5zfSm + mFOsVMg0vL9I4QQtOSx9tL5MXq+zPeRLVpeRvvUD67+wKkf9n+e1DByqCVfaF68U + DDq0L0VQgp3fRNEzLjcXlOOIQ4W/qc1lnxoxVCzQuLJwkZejokV9cj5JDBojmAuK + g+IDL1aArzzKMD5iAAqm0rDbDnMhn0Km+AshDEWgAqtnsVEBRlt+GDAc+d0nplLY + BR8UsaLdtAVHaivXCaZGpjiOsvdOpTwWOaU9HOkuf/1uX5DTaxtClt2BXAnxF3Ug + iQ== -----END CERTIFICATE----- EOF @@ -232,59 +232,59 @@ vault: cat << EOF | /bin/vault kv put secret/sokrates/daps/my-sokrates-daps-key content=- -----BEGIN PRIVATE KEY----- - MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM - wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ - FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 - 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ - ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE - sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc - RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z - d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm - hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm - cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh - FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F - MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e - uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb - ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 - z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 - h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ - vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB - 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 - hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 - dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 - Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 - IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT - 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr - 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 - u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B - AjWFbUiBCFOo+gpAFcQGrkOQHA== + MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwq9Id70+ZDZ8F + oQJhkbxBKN/JysolwRsdBpXqWE6CpJTILRiF9uTpDq1yaquAc8m8Tduw6Fr1PQxI + scpZAm4cJsJqSNyIRfMOnFf6HWmHUN7RHmes1mF6isfjFVkx0PAODY8uuD1jQ1gW + Zmhr8XhOPIuxCZ56zDOQhgwCmwPyXdZ5RcWqvYRomgh2tpAjSnX6yemVHWuaz5Sy + ZOWrd1T8b0SKU+dBqdz+D7XcKcCB8Gu8tcUwXON2xH3A9ZZkfVZJMQl/JYOFdTgS + 8dRlcKFGQZDfD+Dzk7foEKuxWw2wRHjkpihCwt03PA/CQhr/eLBIOqPuMs4Kb8iL + zMlfLhnBAgMBAAECggEAVYco1mMXRsIoXQJAc9moqGbQSBGLYVGl/ZxFkUik4Wwp + turV92y6DvWTFFP9qNblL+sFUxR5jEW8n6iqjAK4KZq9/dQ+Jx6t90HK+YOppd+J + rvUoPa0fTcLH1/Bq2MoMnNEFoxmAZoCgsV9sZ+1jT4TSH6fHeC1JPUsXn19KPtdO + 0b0XvRCVU4sPpzXeaRypnwTsDMgHUoGvxoHQ7Pif5iTnEdgvc7V3ACWOanp/bEuM + 9hoHquggrO/F8SDC4wjn4BlwsxedQZyVF4a76iGS3D/CFrYd8cUKJyCtGySEl7jS + kIwDoG4oQV5mLFSLaq1BDOo8W5ku9JXAW2DZiEgkPQKBgQDav9QTSOp+gqfCDMhT + c45wxYfLfR5QCS5BLufdMmlocL/DzTHTsVddGnOGLoDr8Dbm9nL/vcPmgRtZ0crD + aGqn7sgmbpN6jMsnXhGuOhPZt7Folfbkhv6EFfyjeTZdY4vacrINp97rdVbRvNLs + pDJiHE7PjDTCJh8q2gcWqgc+vwKBgQDOwaJ8NvnnUwrBGzkUHdM4bGunxlK3VV0s + r1BFkmLXbF0qr8sTaUtBj3rbfvMe9R/5hGcuAyDWo+MyVoHc0nzkU1jQwqObLUZB + kg9ZJj4qmnGKnd39TfhDoEluBnl+iYhbXav/2F5eB2UpR0c+DHPrsreGdI3s7O9O + aLL+x5FHfwKBgQCNDwhdyzZTkENHkeCYV7rxo58WrD8g01q9c9bWv8xTKemvBKHt + 5bz1b7oxO8ms24E73I55tdAe0wBlIjDDY5Dra8IrbkCx1Rqn7zQtiowEaD0BuTq1 + UQvM9zSr4d0ZybiEjFOfFLJeWZM7uqy1JojK1YBIvBvFWrnccy4BAnGblwKBgQC5 + GHLNfy4koJw9GpDz6GuC1NVgAtVkWaCrc1uKnS2tq86Qe4ZzH02HKNsVC8a9jTcN + 2zG/6H8KiPfJxdZGiY3TnqYhZk6vik2eQBNLfUgkPdWuAfyNW7MJX8K9JEC6Pof7 + O5XS2rJIvZgb5zrpWp6ggIN6dHfmhosKiALOwnzWIwKBgBsvToOVMzL/7IL4VtSj + u9P+g2shtg9w1dpnscHxUVi2fbKeRRmv2AT140lVpSznIPUmw6FVFUhqE1OTnu1c + qS53otAdwiHAfmYz8u0dZeO0Hc5g4K9geB/BsSthXo3u10HuIVLxefKq2M+3zfJj + ZvNovy5dPYu82VTfm3gUX+Ca -----END PRIVATE KEY----- EOF cat << EOF | /bin/vault kv put secret/sokrates/daps/my-sokrates-daps-crt content=- -----BEGIN CERTIFICATE----- - MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + MIIEAzCCAuugAwIBAgIULy0aTdGiGkyvVp7l5Ccoq7DQREgwDQYJKoZIhvcNAQEL BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ - TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + TjEyMzQwHhcNMjMwNDI2MTI1OTE5WhcNMzMwNDIzMTI1OTE5WjCBkDELMAkGA1UE BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ - KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw - 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa - llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV - grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 - PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 - ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT - MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH - LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL - BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd - iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E - 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 - S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r - uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB - UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + KoZIhvcNAQEBBQADggEPADCCAQoCggEBALCr0h3vT5kNnwWhAmGRvEEo38nKyiXB + Gx0GlepYToKklMgtGIX25OkOrXJqq4BzybxN27DoWvU9DEixylkCbhwmwmpI3IhF + 8w6cV/odaYdQ3tEeZ6zWYXqKx+MVWTHQ8A4Njy64PWNDWBZmaGvxeE48i7EJnnrM + M5CGDAKbA/Jd1nlFxaq9hGiaCHa2kCNKdfrJ6ZUda5rPlLJk5at3VPxvRIpT50Gp + 3P4PtdwpwIHwa7y1xTBc43bEfcD1lmR9VkkxCX8lg4V1OBLx1GVwoUZBkN8P4POT + t+gQq7FbDbBEeOSmKELC3Tc8D8JCGv94sEg6o+4yzgpvyIvMyV8uGcECAwEAAaNT + MFEwHQYDVR0OBBYEFIxEsuJTl+5V8vTCUhMGhWsmdQShMB8GA1UdIwQYMBaAFIxE + suJTl+5V8vTCUhMGhWsmdQShMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAJHOKmFNZDk5xebzBARgcIrYrmRb5pIU4gNCWh/q1TF0+CFnnK8RTFTZ + 12pTbid6v5knn/f9bsilnudhxBzBQ4bukiI8Be0nzYfZU2dTU+w1cl/JnJfkGirt + 8Nwqv3fiUXfFBl8nE0RduAk9XF/UBIZXPapE6u1zR29jvuV+ppmhQrFFeJufeBGd + Wwn6XGK4fzENGDyjdk4QB/dg3/heM5h330vIGO4hVvlQBfJhNbC7Iikkr5ulytfd + deuZIfa7hG6WgIgGhg3YL1p/TTpJamBDS860PWyI7RH3o53VPphu/y2Rpud5AECV + xcrqaSGUTZPVyTUB8BxE32LqFDbpZb4= -----END CERTIFICATE----- EOF } From fcb4694e7eaf10de6016c29f7b8ef386973a9e6a Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Wed, 26 Apr 2023 18:34:51 +0200 Subject: [PATCH 119/132] chore(test): use new certificate in the deployment test (#288) --- .github/workflows/deploy-test-secrets | 84 +++++++++---------- charts/tractusx-connector-memory/example.yaml | 2 +- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.github/workflows/deploy-test-secrets b/.github/workflows/deploy-test-secrets index 28596b459..8fa03fbc5 100644 --- a/.github/workflows/deploy-test-secrets +++ b/.github/workflows/deploy-test-secrets @@ -1,51 +1,51 @@ daps-key:-----BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM -wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ -FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 -8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ -ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE -sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc -RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z -d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm -hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm -cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh -FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F -MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e -uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb -ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 -z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 -h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ -vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB -8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 -hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 -dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 -Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 -IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT -3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr -0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 -u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B -AjWFbUiBCFOo+gpAFcQGrkOQHA== +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDBqOWHLtR/pXk/ +tQ2Zvl9IvS4ku3UQh6oS4sxNzwnpWECC/tYa1EmJnJIgV9BUFzCLM7QL6nPIddj3 +nsckoVaG6KIAKY7U0E5C4XlMJ79f0wdQ58rWXnaYO7rNeFWk9GfxljsIG6X/3hZv +9JLT5InqiCR0HyfAj77cRPXrpa6/VpJ0YoBzZ1qx5DVidq588vbOJVAN8+HnX71V +Qby27AavBqAWtrAOTkWRkHtn6c9nRamYs3ZD6hVEVpKANG5xPHp6Myza0xIrMkgm +MhXOAdEhQvI9IxLcCXAe5vcXBmnZOteiVzMMlYv5cht8d/Dooo1YZHzT9EzjnsCE +3MCl3QZhAgMBAAECggEBAKR/RphRWwciE5/dtrPFVUKAD1X8NS/ZTMnGBCyDlLO0 +1vduZ4dakyxk5mq6rKcBG6biQClu+PJpx+Zt5FJlCQ6HRDRHGKAEYLXGuDXL/W7z +3d8HRPBaRPqCoeYuNPFs+W3oYjQ86AAzMXPfl2iNU+j3w58vZ6DVeRW5LfsAPTMg +Z8Sooa1jD2a/7uDN4lC0FGkTWif//Dio5tbijqeG8xqBnS8iKi4hgxcQA9azd0KB +6uwvbX/izq4sVR2ZjxtT9WPX1cpOcXjUZBM9px9eAwLPmsM/AUAOHkKkd1DPYLjX +yyB0qvz+LmUQdJv11yGagsW7lrrvsBsro4ZMp0Ot1wECgYEA5j2XFCKNUcX/8OFm +8E9q6DXyrd9T3rMxPYWR9nRwV0upN9Zd9mnvOKnl5MYQSgP0XJgwwyHawmG3wIcx +0puf3uWi2lSpt6aafMCW6JEJbK/49XSPAjrptwkZUcCT3XJv1tMZuXzhv/p4t24o +hw9/EtzVxK7thGGZD6sDsQtbOlkCgYEA11OUofuD1VWN5YwFciPv0RhyfDyYYK7e +nPMXEoiBMQJnGkp3eaUzUgej/V93VtJcg9h0Tqn6NpI4rWUfUdi5ihZ6+hcvUIO4 +Roh+Oxpmu0yBfuBo7Uwf5XMpoQu74Z+cr24Pv32YtEUshUZidMuvOMaBXNJGlKiG +DjbCUV0CG0kCgYAVHvlJA5JrOfqsokDLMr3f53MHuED9YPrXZfVp4myb1XkEgknE +XRtw20UXo4PDBnHYPK3ceLKUuloc80oCw/v6ep5h4PpguovZfeFaHFP9AHeaLMMh +tT3TaKZF9aCa4/CWiG8HsQkUj2mbiiN1oFpL5K5HiLSJPFrKMSn5h80qoQKBgQC2 +obt1UEDXFwONaJ/N2dE0RkoEOdj8WBWUhVJSc9kv2lvcnsCLOqU2tChRZUFxMGcr +pNGxTtZcptTPrO9NmkZ0avDPYg7NeYs4t9hpBNGRlyhWlrwoWOLM2Eq8v5kRmzFo +Ui+lOT/l1q4WNEaZzZDG1Qcv1WHsAKwDLkrOe9ankQKBgQDM1fdqraKN2lCkDSPU +/Uw5nmFA7gNJQ9ta6CVITlDMWFb+e2OcDK7pKT1iEhJAfGndtQ0lwK6I5VDDxhXY +DGcU2UIWMAiJOZILDVjkny9brrIQ/fTwZps2qWNJ0bmYsmwPCe9QskNWz8sYAY6p +eBB+WUqNNqBb25p2CmcwqoT7Tg== -----END PRIVATE KEY-----;daps-crt:-----BEGIN CERTIFICATE----- -MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL +MIIEAzCCAuugAwIBAgIULy0aTdGiGkyvVp7l5Ccoq7DQREgwDQYJKoZIhvcNAQEL BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ -TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE +TjEyMzQwHhcNMjMwNDI2MTI1OTE5WhcNMzMwNDIzMTI1OTE5WjCBkDELMAkGA1UE BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw -78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa -llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV -grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 -PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 -ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT -MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH -LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd -iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E -28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 -S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r -uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB -UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALCr0h3vT5kNnwWhAmGRvEEo38nKyiXB +Gx0GlepYToKklMgtGIX25OkOrXJqq4BzybxN27DoWvU9DEixylkCbhwmwmpI3IhF +8w6cV/odaYdQ3tEeZ6zWYXqKx+MVWTHQ8A4Njy64PWNDWBZmaGvxeE48i7EJnnrM +M5CGDAKbA/Jd1nlFxaq9hGiaCHa2kCNKdfrJ6ZUda5rPlLJk5at3VPxvRIpT50Gp +3P4PtdwpwIHwa7y1xTBc43bEfcD1lmR9VkkxCX8lg4V1OBLx1GVwoUZBkN8P4POT +t+gQq7FbDbBEeOSmKELC3Tc8D8JCGv94sEg6o+4yzgpvyIvMyV8uGcECAwEAAaNT +MFEwHQYDVR0OBBYEFIxEsuJTl+5V8vTCUhMGhWsmdQShMB8GA1UdIwQYMBaAFIxE +suJTl+5V8vTCUhMGhWsmdQShMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAJHOKmFNZDk5xebzBARgcIrYrmRb5pIU4gNCWh/q1TF0+CFnnK8RTFTZ +12pTbid6v5knn/f9bsilnudhxBzBQ4bukiI8Be0nzYfZU2dTU+w1cl/JnJfkGirt +8Nwqv3fiUXfFBl8nE0RduAk9XF/UBIZXPapE6u1zR29jvuV+ppmhQrFFeJufeBGd +Wwn6XGK4fzENGDyjdk4QB/dg3/heM5h330vIGO4hVvlQBfJhNbC7Iikkr5ulytfd +deuZIfa7hG6WgIgGhg3YL1p/TTpJamBDS860PWyI7RH3o53VPphu/y2Rpud5AECV +xcrqaSGUTZPVyTUB8BxE32LqFDbpZb4= -----END CERTIFICATE----- \ No newline at end of file diff --git a/charts/tractusx-connector-memory/example.yaml b/charts/tractusx-connector-memory/example.yaml index 57d12b039..243ba64a4 100644 --- a/charts/tractusx-connector-memory/example.yaml +++ b/charts/tractusx-connector-memory/example.yaml @@ -59,7 +59,7 @@ vault: daps: url: "http://ids-daps:4567" - clientId: "E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65" + clientId: "99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23:keyid:99:83:A7:17:86:FF:98:93:CE:A0:DD:A1:F1:36:FA:F6:0F:75:0A:23" backendService: httpProxyTokenReceiverUrl: "http://backend:8080" From 7dceee5f6e2c76ab16e8a14a37267f0eb3b4e54e Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Wed, 26 Apr 2023 18:34:59 +0200 Subject: [PATCH 120/132] chore(build): add GHA variables for sonar project and org (#287) * chore(build): add GHA variables for sonar project and org * trigger ci --- .github/workflows/verify.yaml | 4 ++-- README.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index f24fee8d4..e2409f542 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -151,8 +151,8 @@ jobs: run: |- ./gradlew sonar \ -Pcoverage,failsafe \ - -Dsonar.projectKey=${GITHUB_REPOSITORY_OWNER}_product-edc \ - -Dsonar.organization=${GITHUB_REPOSITORY_OWNER} \ + -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} \ + -Dsonar.organization=${{ vars.SONAR_ORGANIZATION }} \ -Dsonar.host.url=https://sonarcloud.io \ -Dsonar.coverage.jacoco.xmlReportPaths=${GITHUB_WORKSPACE}/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml \ -Dsonar.verbose=true diff --git a/README.md b/README.md index d1c6a0422..9411054ef 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ [![Apache 2.0 License][license-shield]][license-url] [![Latest Release][release-shield]][release-url] +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=eclipse-tractusx_tractusx-edc&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=eclipse-tractusx_tractusx-edc) + Container images and deployments of the Eclipse Dataspace Components for the Tractus-X project. Please also refer to: From 59de4e24a0077f7c714846a872075556893189cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 07:22:13 +0200 Subject: [PATCH 121/132] chore(deps): bump org.junit:junit-bom from 5.9.2 to 5.9.3 (#290) Bumps [org.junit:junit-bom](https://github.com/junit-team/junit5) from 5.9.2 to 5.9.3. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.2...r5.9.3) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index f0ca42f83..77de11e65 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -55,7 +55,7 @@ allprojects { implementation("org.slf4j:slf4j-api:2.0.7") // this is used to counter version conflicts between the JUnit version pulled in by the plugin, // and the one expected by IntelliJ - testImplementation(platform("org.junit:junit-bom:5.9.2")) + testImplementation(platform("org.junit:junit-bom:5.9.3")) constraints { implementation("org.yaml:snakeyaml:2.0") { From a276a6349dfd4f12ade35ccd92e7e9eb627d885e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 07:22:24 +0200 Subject: [PATCH 122/132] chore(deps): bump org.junit.platform:junit-platform-suite (#291) Bumps [org.junit.platform:junit-platform-suite](https://github.com/junit-team/junit5) from 1.9.2 to 1.9.3. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/commits) --- updated-dependencies: - dependency-name: org.junit.platform:junit-platform-suite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-tests/cucumber/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index 2628ce71e..5d80a8a6e 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { testImplementation("com.google.code.gson:gson:2.10.1") testImplementation("org.apache.httpcomponents:httpclient:4.5.14") - testImplementation("org.junit.platform:junit-platform-suite:1.9.2") + testImplementation("org.junit.platform:junit-platform-suite:1.9.3") testImplementation("io.cucumber:cucumber-java:7.11.2") testImplementation("io.cucumber:cucumber-junit-platform-engine:7.11.2") testImplementation("org.slf4j:slf4j-api:2.0.7") From ce48a77ad559a061acddf488ad1310cadaff4901 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Thu, 27 Apr 2023 09:30:40 +0200 Subject: [PATCH 123/132] chore(build): remove CI-triggered sonar job (#292) --- .github/workflows/build.yaml | 2 -- .github/workflows/verify.yaml | 44 ----------------------------------- build.gradle.kts | 13 ----------- 3 files changed, 59 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9b14af068..d897a1433 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -52,7 +52,6 @@ jobs: secret-presence: runs-on: ubuntu-latest outputs: - SONAR_TOKEN: ${{ steps.secret-presence.outputs.SONAR_TOKEN }} GPG_PRIVATE_KEY: ${{ steps.secret-presence.outputs.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ steps.secret-presence.outputs.GPG_PASSPHRASE }} DOCKER_HUB_TOKEN: ${{ steps.secret-presence.outputs.DOCKER_HUB_TOKEN }} @@ -60,7 +59,6 @@ jobs: - name: Check whether secrets exist id: secret-presence run: | - [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "SONAR_TOKEN=true" >> $GITHUB_OUTPUT [ ! -z "${{ secrets.GPG_PRIVATE_KEY }}" ] && echo "GPG_PRIVATE_KEY=true" >> $GITHUB_OUTPUT [ ! -z "${{ secrets.GPG_PASSPHRASE }}" ] && echo "GPG_PASSPHRASE=true" >> $GITHUB_OUTPUT [ ! -z "${{ secrets.DOCKER_HUB_TOKEN }}" ] && echo "DOCKER_HUB_TOKEN=true" >> $GITHUB_OUTPUT diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index e2409f542..7e27b85a0 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -44,16 +44,6 @@ concurrency: cancel-in-progress: true jobs: - secret-presence: - runs-on: ubuntu-latest - outputs: - SONAR_TOKEN: ${{ steps.secret-presence.outputs.SONAR_TOKEN }} - steps: - - name: Check whether secrets exist - id: secret-presence - run: | - [ ! -z "${{ secrets.SONAR_TOKEN }}" ] && echo "SONAR_TOKEN=true" >> $GITHUB_OUTPUT - exit 0 verify-formatting: runs-on: ubuntu-latest @@ -124,37 +114,3 @@ jobs: - name: Run E2E tests run: ./gradlew :edc-tests:runtime:build test -DincludeTags="EndToEndTest" - - sonar: - needs: [ secret-presence, verify-formatting ] - if: | - needs.secret-presence.outputs.SONAR_TOKEN - runs-on: ubuntu-latest - steps: - # Set-Up - - uses: actions/checkout@v3.5.2 - with: - fetch-depth: 0 - - uses: ./.github/actions/setup-java - - name: Cache SonarCloud packages - uses: actions/cache@v3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - # Analyse - - name: Build with Maven and analyze with Sonar - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - JACOCO: true - run: |- - ./gradlew sonar \ - -Pcoverage,failsafe \ - -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} \ - -Dsonar.organization=${{ vars.SONAR_ORGANIZATION }} \ - -Dsonar.host.url=https://sonarcloud.io \ - -Dsonar.coverage.jacoco.xmlReportPaths=${GITHUB_WORKSPACE}/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml \ - -Dsonar.verbose=true - - diff --git a/build.gradle.kts b/build.gradle.kts index 77de11e65..454796c04 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,6 @@ plugins { id("com.diffplug.spotless") version "6.18.0" id("com.github.johnrengelman.shadow") version "8.1.1" id("com.bmuschko.docker-remote-api") version "9.3.1" - id("org.sonarqube") version "4.0.0.2929" } val javaVersion: String by project @@ -37,15 +36,9 @@ project.subprojects.forEach { } } -// make sure the test report aggregation is done before reporting to sonar -tasks.sonar { - dependsOn(tasks.named("testCodeCoverageReport")) -} - allprojects { apply(plugin = "org.eclipse.edc.edc-build") apply(plugin = "io.freefair.lombok") - apply(plugin = "org.sonarqube") repositories { mavenCentral() @@ -161,10 +154,4 @@ subprojects { dockerTask.dependsOn(tasks.findByName(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME)) } } - - sonarqube { - properties { - property("sonar.moduleKey", "${project.group}-${project.name}") - } - } } From eb7fccb1d103819508c76c2a1b5f4447e9f9b3f7 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Fri, 28 Apr 2023 15:41:21 +0200 Subject: [PATCH 124/132] chore: prepare Changelog and Migr. Guide for 0.3.4 (#298) * chore: prepare Changelog and Migr. Guide for 0.3.4 * Update docs/migration/Version_0.3.3_0.3.4.md Co-authored-by: Tuncay Tunc (ZF Friedrichshafen AG) <100704677+tuncaytunc-zf@users.noreply.github.com> --------- Co-authored-by: Tuncay Tunc (ZF Friedrichshafen AG) <100704677+tuncaytunc-zf@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ docs/migration/Version_0.3.3_0.3.4.md | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 docs/migration/Version_0.3.3_0.3.4.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ce0ca990..316589f06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.3.3] - 2023-04-28 + +### Fixed + +- Added license headers to several files in the code base +- Refactoring of Helm charts - multiple charts instead of one dynamically assembled chart + ## [0.3.3] - 2023-04-19 ### Fixed diff --git a/docs/migration/Version_0.3.3_0.3.4.md b/docs/migration/Version_0.3.3_0.3.4.md new file mode 100644 index 000000000..a93de9600 --- /dev/null +++ b/docs/migration/Version_0.3.3_0.3.4.md @@ -0,0 +1,21 @@ +# Migration from 0.3.3 to 0.3.4 + +## Refactoring of Helm Charts + +In issue [#136](https://github.com/eclipse-tractusx/tractusx-edc/issues/136) work has begun to split the Helm charts up +into several technology-focused charts: + +- In-memory: for testing and development +- PostgreSQL+Hashicorp: this is the **recommended** distribution of Tractus-X EDC +- (Azure KeyVault: uses Azure KeyVault instead of Hashicorp Vault.) - Work in Progress + +Unfortunately, due to time constraints, we had to release 0.3.4 **without** the Azure KeyVault chart, it will be +included in one of the subsequent releases in the future. + +**Please note that the Azure KeyVault variant is not included in the 0.3.4 Release! If you rely on AZKV please do NOT +upgrade to 0.3.4 yet!** + +## Change in Docker image publishing + +Starting with the 0.3.3 release we switched over to publish our Docker images +to [Docker Hub](https://hub.docker.com/search?q=tractusx) instead of GHCR. From 57da0cb124a3efea54d7fe5059d2b33b816167bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 18:51:09 +0200 Subject: [PATCH 125/132] chore(deps): bump org.flywaydb:flyway-core from 9.16.3 to 9.17.0 (#294) Bumps [org.flywaydb:flyway-core](https://github.com/flyway/flyway) from 9.16.3 to 9.17.0. - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/commits/flyway-9.17.0) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-extensions/postgresql-migration/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-extensions/postgresql-migration/build.gradle.kts b/edc-extensions/postgresql-migration/build.gradle.kts index cb04877c0..88db6be4e 100644 --- a/edc-extensions/postgresql-migration/build.gradle.kts +++ b/edc-extensions/postgresql-migration/build.gradle.kts @@ -11,5 +11,5 @@ dependencies { implementation(edc.sql.assetindex) implementation(edc.sql.core) - implementation("org.flywaydb:flyway-core:9.16.3") + implementation("org.flywaydb:flyway-core:9.17.0") } From 35a2bcf3d536468f9a64edd5ad0d0d4d937db8fa Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Tue, 2 May 2023 11:23:36 +0200 Subject: [PATCH 126/132] refactor(chart): move test infrastructure into the test chart (#299) --- .github/workflows/deployment-test.yaml | 8 +- charts/tractusx-connector-app/.helmignore | 23 - charts/tractusx-connector-app/Chart.yaml | 70 -- charts/tractusx-connector-app/README.md | 237 ------- charts/tractusx-connector-app/example.yaml | 190 ------ charts/tractusx-connector-app/values.yaml | 532 --------------- .../helm/test-infrastructure/Chart.yaml | 12 + .../helm/test-infrastructure/values.yaml | 614 +++++++++++++++++- .../helm/tractusx-connector-test.yaml | 82 +++ 9 files changed, 707 insertions(+), 1061 deletions(-) delete mode 100644 charts/tractusx-connector-app/.helmignore delete mode 100644 charts/tractusx-connector-app/Chart.yaml delete mode 100644 charts/tractusx-connector-app/README.md delete mode 100644 charts/tractusx-connector-app/example.yaml delete mode 100644 charts/tractusx-connector-app/values.yaml create mode 100644 edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml diff --git a/.github/workflows/deployment-test.yaml b/.github/workflows/deployment-test.yaml index 1cde6d4f5..ad7e65fdf 100644 --- a/.github/workflows/deployment-test.yaml +++ b/.github/workflows/deployment-test.yaml @@ -88,14 +88,14 @@ jobs: imagename: "edc-controlplane-postgresql-hashicorp-vault edc-dataplane-hashicorp-vault" rootDir: "." helm_command: |- - helm install tx-prod charts/tractusx-connector-app \ - -f charts/tractusx-connector-app/example.yaml \ + helm install tx-prod charts/tractusx-connector \ + -f edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml \ --dependency-update \ --wait-for-jobs --timeout=120s # wait for the pod to become ready - kubectl rollout status deployment tx-prod-runtime-controlplane - kubectl rollout status deployment tx-prod-runtime-dataplane + kubectl rollout status deployment tx-prod-controlplane + kubectl rollout status deployment tx-prod-dataplane # execute the helm test helm test tx-prod --logs diff --git a/charts/tractusx-connector-app/.helmignore b/charts/tractusx-connector-app/.helmignore deleted file mode 100644 index 0e8a0eb36..000000000 --- a/charts/tractusx-connector-app/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/charts/tractusx-connector-app/Chart.yaml b/charts/tractusx-connector-app/Chart.yaml deleted file mode 100644 index cdd48977d..000000000 --- a/charts/tractusx-connector-app/Chart.yaml +++ /dev/null @@ -1,70 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - ---- -apiVersion: v2 -name: tractusx-connector-app -description: | - A Helm chart for Tractus-X Eclipse Data Space Connector Application. This includes the runtime, which consists of a control plane - and a data plane, and all third-party services such as PostgreSQL and HashiCorp Vault. - - This chart is intended to be used as self-contained deployment, which only requires an external DAPS instance. -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.2 -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "0.3.2" -home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-app -sources: - - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-app - -dependencies: - # EDC Connector Runtime (ControlPlane + DataPlane) - - name: tractusx-connector - version: "0.3.3" - alias: runtime - repository: "file://../tractusx-connector" - - # HashiCorp Vault - - name: vault - alias: vault - version: 0.20.0 - repository: https://helm.releases.hashicorp.com - - # PostgreSQL - - name: postgresql - alias: postgresql - version: 12.1.6 - repository: https://charts.bitnami.com/bitnami diff --git a/charts/tractusx-connector-app/README.md b/charts/tractusx-connector-app/README.md deleted file mode 100644 index 56af7fdcf..000000000 --- a/charts/tractusx-connector-app/README.md +++ /dev/null @@ -1,237 +0,0 @@ -# tractusx-connector-app - -![Version: 0.3.2](https://img.shields.io/badge/Version-0.3.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.2](https://img.shields.io/badge/AppVersion-0.3.2-informational?style=flat-square) - -A Helm chart for Tractus-X Eclipse Data Space Connector Application. This includes the runtime, which consists of a control plane -and a data plane, and all third-party services such as PostgreSQL and HashiCorp Vault. - -This chart is intended to be used as self-contained deployment, which only requires an external DAPS instance. - -**Homepage:** - -## Source Code - -* - -## Requirements - -| Repository | Name | Version | -|------------|------|---------| -| file://../tractusx-connector | runtime(tractusx-connector) | 0.3.2 | -| https://charts.bitnami.com/bitnami | postgresql(postgresql) | 12.1.6 | -| https://helm.releases.hashicorp.com | vault(vault) | 0.20.0 | - -## Values - -| Key | Type | Default | Description | -|-----|------|---------|-------------| -| customLabels | object | `{}` | | -| fullnameOverride | string | `""` | | -| imagePullSecrets | list | `[]` | Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | -| nameOverride | string | `""` | | -| runtime.backendService.httpProxyTokenReceiverUrl | string | `""` | | -| runtime.controlplane.affinity | object | `{}` | | -| runtime.controlplane.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | -| runtime.controlplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| runtime.controlplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| runtime.controlplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| runtime.controlplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| runtime.controlplane.debug.enabled | bool | `false` | | -| runtime.controlplane.debug.port | int | `1044` | | -| runtime.controlplane.debug.suspendOnStart | bool | `false` | | -| runtime.controlplane.endpoints | object | `{"control":{"path":"/control","port":8083},"default":{"path":"/api","port":8080},"management":{"authKey":"","path":"/management","port":8081},"metrics":{"path":"/metrics","port":9090},"observability":{"insecure":true,"path":"/observability","port":8085},"protocol":{"path":"/api/v1/ids","port":8084}}` | endpoints of the control plane | -| runtime.controlplane.endpoints.control | object | `{"path":"/control","port":8083}` | control api, used for internal control calls. can be added to the internal ingress, but should probably not | -| runtime.controlplane.endpoints.control.path | string | `"/control"` | path for incoming api calls | -| runtime.controlplane.endpoints.control.port | int | `8083` | port for incoming api calls | -| runtime.controlplane.endpoints.default | object | `{"path":"/api","port":8080}` | default api for health checks, should not be added to any ingress | -| runtime.controlplane.endpoints.default.path | string | `"/api"` | path for incoming api calls | -| runtime.controlplane.endpoints.default.port | int | `8080` | port for incoming api calls | -| runtime.controlplane.endpoints.management | object | `{"authKey":"","path":"/management","port":8081}` | data management api, used by internal users, can be added to an ingress and must not be internet facing | -| runtime.controlplane.endpoints.management.authKey | string | `""` | authentication key, must be attached to each 'X-Api-Key' request header | -| runtime.controlplane.endpoints.management.path | string | `"/management"` | path for incoming api calls | -| runtime.controlplane.endpoints.management.port | int | `8081` | port for incoming api calls | -| runtime.controlplane.endpoints.metrics | object | `{"path":"/metrics","port":9090}` | metrics api, used for application metrics, must not be internet facing | -| runtime.controlplane.endpoints.metrics.path | string | `"/metrics"` | path for incoming api calls | -| runtime.controlplane.endpoints.metrics.port | int | `9090` | port for incoming api calls | -| runtime.controlplane.endpoints.observability | object | `{"insecure":true,"path":"/observability","port":8085}` | observability api with unsecured access, must not be internet facing | -| runtime.controlplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | -| runtime.controlplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | -| runtime.controlplane.endpoints.observability.port | int | `8085` | port for incoming API calls | -| runtime.controlplane.endpoints.protocol | object | `{"path":"/api/v1/ids","port":8084}` | ids api, used for inter connector communication and must be internet facing | -| runtime.controlplane.endpoints.protocol.path | string | `"/api/v1/ids"` | path for incoming api calls | -| runtime.controlplane.endpoints.protocol.port | int | `8084` | port for incoming api calls | -| runtime.controlplane.env | object | `{}` | | -| runtime.controlplane.envConfigMapNames | list | `[]` | | -| runtime.controlplane.envSecretNames | list | `[]` | | -| runtime.controlplane.envValueFrom | object | `{}` | | -| runtime.controlplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| runtime.controlplane.image.repository | string | `""` | Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically | -| runtime.controlplane.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | -| runtime.controlplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | -| runtime.controlplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| runtime.controlplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| runtime.controlplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| runtime.controlplane.ingresses[0].enabled | bool | `false` | | -| runtime.controlplane.ingresses[0].endpoints | list | `["ids"]` | EDC endpoints exposed by this ingress resource | -| runtime.controlplane.ingresses[0].hostname | string | `"edc-control.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| runtime.controlplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| runtime.controlplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| runtime.controlplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | -| runtime.controlplane.ingresses[1].annotations | object | `{}` | Additional ingress annotations to add | -| runtime.controlplane.ingresses[1].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| runtime.controlplane.ingresses[1].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| runtime.controlplane.ingresses[1].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| runtime.controlplane.ingresses[1].enabled | bool | `false` | | -| runtime.controlplane.ingresses[1].endpoints | list | `["management","control"]` | EDC endpoints exposed by this ingress resource | -| runtime.controlplane.ingresses[1].hostname | string | `"edc-control.intranet"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| runtime.controlplane.ingresses[1].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| runtime.controlplane.ingresses[1].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| runtime.controlplane.ingresses[1].tls.secretName | string | `""` | If present overwrites the default secret name | -| runtime.controlplane.initContainers | list | `[]` | | -| runtime.controlplane.internationalDataSpaces.catalogId | string | `"TXDC-Catalog"` | | -| runtime.controlplane.internationalDataSpaces.curator | string | `""` | | -| runtime.controlplane.internationalDataSpaces.description | string | `"Tractus-X Eclipse IDS Data Space Connector"` | | -| runtime.controlplane.internationalDataSpaces.id | string | `"TXDC"` | | -| runtime.controlplane.internationalDataSpaces.maintainer | string | `""` | | -| runtime.controlplane.internationalDataSpaces.title | string | `""` | | -| runtime.controlplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| runtime.controlplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | -| runtime.controlplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | -| runtime.controlplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | -| runtime.controlplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | -| runtime.controlplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| runtime.controlplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | -| runtime.controlplane.nodeSelector | object | `{}` | | -| runtime.controlplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | -| runtime.controlplane.podAnnotations | object | `{}` | additional annotations for the pod | -| runtime.controlplane.podLabels | object | `{}` | additional labels for the pod | -| runtime.controlplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | -| runtime.controlplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | -| runtime.controlplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | -| runtime.controlplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | -| runtime.controlplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | -| runtime.controlplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| runtime.controlplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | -| runtime.controlplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | -| runtime.controlplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a readiness check every 10 seconds | -| runtime.controlplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | -| runtime.controlplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| runtime.controlplane.replicaCount | int | `1` | | -| runtime.controlplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | -| runtime.controlplane.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | -| runtime.controlplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | -| runtime.controlplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | -| runtime.controlplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | -| runtime.controlplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| runtime.controlplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | -| runtime.controlplane.service.annotations | object | `{}` | | -| runtime.controlplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| runtime.controlplane.tolerations | list | `[]` | | -| runtime.controlplane.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | -| runtime.controlplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | -| runtime.controlplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | -| runtime.daps.clientId | string | `""` | | -| runtime.daps.paths.jwks | string | `"/jwks.json"` | | -| runtime.daps.paths.token | string | `"/token"` | | -| runtime.daps.url | string | `""` | | -| runtime.dataplane.affinity | object | `{}` | | -| runtime.dataplane.autoscaling.enabled | bool | `false` | Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) | -| runtime.dataplane.autoscaling.maxReplicas | int | `100` | Maximum replicas if resource consumption exceeds resource threshholds | -| runtime.dataplane.autoscaling.minReplicas | int | `1` | Minimal replicas if resource consumption falls below resource threshholds | -| runtime.dataplane.autoscaling.targetCPUUtilizationPercentage | int | `80` | targetAverageUtilization of cpu provided to a pod | -| runtime.dataplane.autoscaling.targetMemoryUtilizationPercentage | int | `80` | targetAverageUtilization of memory provided to a pod | -| runtime.dataplane.aws.accessKeyId | string | `""` | | -| runtime.dataplane.aws.endpointOverride | string | `""` | | -| runtime.dataplane.aws.secretAccessKey | string | `""` | | -| runtime.dataplane.debug.enabled | bool | `false` | | -| runtime.dataplane.debug.port | int | `1044` | | -| runtime.dataplane.debug.suspendOnStart | bool | `false` | | -| runtime.dataplane.endpoints.control.path | string | `"/api/dataplane/control"` | | -| runtime.dataplane.endpoints.control.port | int | `8083` | | -| runtime.dataplane.endpoints.default.path | string | `"/api"` | | -| runtime.dataplane.endpoints.default.port | int | `8080` | | -| runtime.dataplane.endpoints.metrics.path | string | `"/metrics"` | | -| runtime.dataplane.endpoints.metrics.port | int | `9090` | | -| runtime.dataplane.endpoints.observability.insecure | bool | `true` | allow or disallow insecure access, i.e. access without authentication | -| runtime.dataplane.endpoints.observability.path | string | `"/observability"` | observability api, provides /health /readiness and /liveness endpoints | -| runtime.dataplane.endpoints.observability.port | int | `8085` | port for incoming API calls | -| runtime.dataplane.endpoints.public.path | string | `"/api/public"` | | -| runtime.dataplane.endpoints.public.port | int | `8081` | | -| runtime.dataplane.env | object | `{}` | | -| runtime.dataplane.envConfigMapNames | list | `[]` | | -| runtime.dataplane.envSecretNames | list | `[]` | | -| runtime.dataplane.envValueFrom | object | `{}` | | -| runtime.dataplane.image.pullPolicy | string | `"IfNotPresent"` | [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use | -| runtime.dataplane.image.repository | string | `""` | Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically | -| runtime.dataplane.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion | -| runtime.dataplane.ingresses[0].annotations | object | `{}` | Additional ingress annotations to add | -| runtime.dataplane.ingresses[0].certManager.clusterIssuer | string | `""` | If preset enables certificate generation via cert-manager cluster-wide issuer | -| runtime.dataplane.ingresses[0].certManager.issuer | string | `""` | If preset enables certificate generation via cert-manager namespace scoped issuer | -| runtime.dataplane.ingresses[0].className | string | `""` | Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use | -| runtime.dataplane.ingresses[0].enabled | bool | `false` | | -| runtime.dataplane.ingresses[0].endpoints | list | `["public"]` | EDC endpoints exposed by this ingress resource | -| runtime.dataplane.ingresses[0].hostname | string | `"edc-data.local"` | The hostname to be used to precisely map incoming traffic onto the underlying network service | -| runtime.dataplane.ingresses[0].tls | object | `{"enabled":false,"secretName":""}` | TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource | -| runtime.dataplane.ingresses[0].tls.enabled | bool | `false` | Enables TLS on the ingress resource | -| runtime.dataplane.ingresses[0].tls.secretName | string | `""` | If present overwrites the default secret name | -| runtime.dataplane.initContainers | list | `[]` | | -| runtime.dataplane.livenessProbe.enabled | bool | `true` | Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| runtime.dataplane.livenessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | -| runtime.dataplane.livenessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first liveness check | -| runtime.dataplane.livenessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | -| runtime.dataplane.livenessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | -| runtime.dataplane.livenessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| runtime.dataplane.logging | string | `".level=INFO\norg.eclipse.edc.level=ALL\nhandlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter\njava.util.logging.ConsoleHandler.level=ALL\njava.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n"` | configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) | -| runtime.dataplane.nodeSelector | object | `{}` | | -| runtime.dataplane.opentelemetry | string | `"otel.javaagent.enabled=false\notel.javaagent.debug=false"` | configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics | -| runtime.dataplane.podAnnotations | object | `{}` | additional annotations for the pod | -| runtime.dataplane.podLabels | object | `{}` | additional labels for the pod | -| runtime.dataplane.podSecurityContext | object | `{"fsGroup":10001,"runAsGroup":10001,"runAsUser":10001,"seccompProfile":{"type":"RuntimeDefault"}}` | The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment | -| runtime.dataplane.podSecurityContext.fsGroup | int | `10001` | The owner for volumes and any files created within volumes will belong to this guid | -| runtime.dataplane.podSecurityContext.runAsGroup | int | `10001` | Processes within a pod will belong to this guid | -| runtime.dataplane.podSecurityContext.runAsUser | int | `10001` | Runs all processes within a pod with a special uid | -| runtime.dataplane.podSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | Restrict a Container's Syscalls with seccomp | -| runtime.dataplane.readinessProbe.enabled | bool | `true` | Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) | -| runtime.dataplane.readinessProbe.failureThreshold | int | `6` | when a probe fails kubernetes will try 6 times before giving up | -| runtime.dataplane.readinessProbe.initialDelaySeconds | int | `30` | seconds to wait before performing the first readiness check | -| runtime.dataplane.readinessProbe.periodSeconds | int | `10` | this fields specifies that kubernetes should perform a liveness check every 10 seconds | -| runtime.dataplane.readinessProbe.successThreshold | int | `1` | number of consecutive successes for the probe to be considered successful after having failed | -| runtime.dataplane.readinessProbe.timeoutSeconds | int | `5` | number of seconds after which the probe times out | -| runtime.dataplane.replicaCount | int | `1` | | -| runtime.dataplane.resources | object | `{}` | [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container | -| runtime.dataplane.securityContext.allowPrivilegeEscalation | bool | `false` | Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID | -| runtime.dataplane.securityContext.capabilities.add | list | `[]` | Specifies which capabilities to add to issue specialized syscalls | -| runtime.dataplane.securityContext.capabilities.drop | list | `["ALL"]` | Specifies which capabilities to drop to reduce syscall attack surface | -| runtime.dataplane.securityContext.readOnlyRootFilesystem | bool | `true` | Whether the root filesystem is mounted in read-only mode | -| runtime.dataplane.securityContext.runAsNonRoot | bool | `true` | Requires the container to run without root privileges | -| runtime.dataplane.securityContext.runAsUser | int | `10001` | The container's process will run with the specified uid | -| runtime.dataplane.service.port | int | `80` | | -| runtime.dataplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | -| runtime.dataplane.tolerations | list | `[]` | | -| runtime.dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | -| runtime.dataplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | -| runtime.dataplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | -| runtime.postgresql.enabled | bool | `false` | | -| runtime.postgresql.jdbcUrl | string | `""` | | -| runtime.postgresql.password | string | `""` | | -| runtime.postgresql.username | string | `""` | | -| runtime.serviceAccount.annotations | object | `{}` | | -| runtime.serviceAccount.create | bool | `true` | | -| runtime.serviceAccount.imagePullSecrets | list | `[]` | Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | -| runtime.serviceAccount.name | string | `""` | | -| runtime.vault.hashicorp.enabled | bool | `true` | | -| runtime.vault.hashicorp.healthCheck.enabled | bool | `true` | | -| runtime.vault.hashicorp.healthCheck.standbyOk | bool | `true` | | -| runtime.vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | -| runtime.vault.hashicorp.paths.secret | string | `"/v1/secret"` | | -| runtime.vault.hashicorp.timeout | int | `30` | | -| runtime.vault.hashicorp.token | string | `""` | | -| runtime.vault.hashicorp.url | string | `""` | | -| runtime.vault.secretNames.dapsPrivateKey | string | `"daps-private-key"` | | -| runtime.vault.secretNames.dapsPublicKey | string | `"daps-public-key"` | | -| runtime.vault.secretNames.transferProxyTokenEncryptionAesKey | string | `"transfer-proxy-token-encryption-aes-key"` | | -| runtime.vault.secretNames.transferProxyTokenSignerPrivateKey | string | `"transfer-proxy-token-signer-private-key"` | | -| runtime.vault.secretNames.transferProxyTokenSignerPublicKey | string | `"transfer-proxy-token-signer-public-key"` | | - ----------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) diff --git a/charts/tractusx-connector-app/example.yaml b/charts/tractusx-connector-app/example.yaml deleted file mode 100644 index 280121252..000000000 --- a/charts/tractusx-connector-app/example.yaml +++ /dev/null @@ -1,190 +0,0 @@ -# -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -## This file can be used to verify that the chart is working properly. It provides an exemplary configuration -## that is intended to be used with the supporting infrastructure. -## 1. install DAPS: -## helm install infrastructure edc-tests/deployment/src/main/resources/helm/test-infrastructure --wait-for-jobs -## -## 2. install the connector plus its third-party dependencies (HashiCorp Vault and Postgres): -## helm install tx-prod charts/tractusx-connector-app -f charts/tractusx-connector-app/example.yaml --dependency-update - -fullnameOverride: tx-prod - -################################ -# EDC ControlPlane + DataPlane # -################################ -runtime: - controlplane: - service: - type: NodePort - endpoints: - management: - authKey: password - image: - pullPolicy: Never - tag: "latest" - repository: "edc-controlplane-postgresql-hashicorp-vault" - securityContext: - # avoids some errors in the log: cannot write temp files of large multipart requests when R/O - readOnlyRootFilesystem: false - - dataplane: - image: - pullPolicy: Never - tag: "latest" - repository: "edc-dataplane-hashicorp-vault" - - securityContext: - # avoids some errors in the log: cannot write temp files of large multipart requests when R/O - readOnlyRootFilesystem: false - - aws: - endpointOverride: http://minio:9000 - secretAccessKey: qwerty123 - accessKeyId: qwerty123 - - postgresql: - username: user - password: password - jdbcUrl: jdbc:postgresql://postgresql:5432/edc - - vault: - hashicorp: - url: http://vault:8200 - token: root - - secretNames: - transferProxyTokenSignerPublicKey: daps-crt - transferProxyTokenSignerPrivateKey: daps-key - transferProxyTokenEncryptionAesKey: aes-keys - dapsPrivateKey: daps-key - dapsPublicKey: daps-crt - - # this must be set through CLI args: --set vault.secrets=$YOUR_VAULT_SECRETS where YOUR_VAULT_SECRETS should - # be a string in the format "key1:secret1;key2:secret2;..." - secrets: - - daps: - url: "http://ids-daps:4567" - clientId: "E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65" - - backendService: - httpProxyTokenReceiverUrl: "http://backend:8080" - -############## -# POSTGRESQL # -############## -postgresql: - fullnameOverride: "postgresql" - primary: - persistence: - enabled: false - readReplicas: - persistence: - enabled: false - auth: - database: "edc" - username: "user" - password: "password" - -######### -# VAULT # -######### -vault: - fullnameOverride: "vault" - injector: - enabled: false - server: - dev: - enabled: true - devRootToken: "root" - # Must be the same certificate that is configured in section 'ids-daps' - postStart: - - "sh" - - "-c" - - | - { - - sleep 5 - - /bin/vault kv put secret/sokrates/data-encryption-aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== - - cat << EOF | /bin/vault kv put secret/daps-key content=- - -----BEGIN PRIVATE KEY----- - MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM - wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ - FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 - 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ - ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE - sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc - RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z - d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm - hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm - cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh - FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F - MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e - uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb - ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 - z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 - h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ - vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB - 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 - hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 - dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 - Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 - IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT - 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr - 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 - u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B - AjWFbUiBCFOo+gpAFcQGrkOQHA== - -----END PRIVATE KEY----- - EOF - - cat << EOF | /bin/vault kv put secret/daps-crt content=- - -----BEGIN CERTIFICATE----- - MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL - BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl - cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 - bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ - TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE - BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK - DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD - DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ - KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw - 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa - llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV - grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 - PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 - ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT - MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH - LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL - BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd - iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E - 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 - S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r - uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB - UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= - -----END CERTIFICATE----- - EOF - - /bin/vault kv put secret/aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== - - } diff --git a/charts/tractusx-connector-app/values.yaml b/charts/tractusx-connector-app/values.yaml deleted file mode 100644 index 98a62ef06..000000000 --- a/charts/tractusx-connector-app/values.yaml +++ /dev/null @@ -1,532 +0,0 @@ -# -# Copyright (c) 2023 ZF Friedrichshafen AG -# Copyright (c) 2023 Mercedes-Benz Tech Innovation GmbH -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) -# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# 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 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - - ---- -# Default values for eclipse-dataspace-connector. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -fullnameOverride: "" -nameOverride: "" - -# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) -imagePullSecrets: [] - -customLabels: {} - -runtime: - controlplane: - image: - # -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically - repository: "" - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion - tag: "" - initContainers: [] - debug: - enabled: false - port: 1044 - suspendOnStart: false - internationalDataSpaces: - id: TXDC - description: Tractus-X Eclipse IDS Data Space Connector - title: "" - maintainer: "" - curator: "" - catalogId: TXDC-Catalog - livenessProbe: - # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - # -- seconds to wait before performing the first liveness check - initialDelaySeconds: 30 - # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds - periodSeconds: 10 - # -- number of seconds after which the probe times out - timeoutSeconds: 5 - # -- when a probe fails kubernetes will try 6 times before giving up - failureThreshold: 6 - # -- number of consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - readinessProbe: - # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - # -- seconds to wait before performing the first readiness check - initialDelaySeconds: 30 - # -- this fields specifies that kubernetes should perform a readiness check every 10 seconds - periodSeconds: 10 - # -- number of seconds after which the probe times out - timeoutSeconds: 5 - # -- when a probe fails kubernetes will try 6 times before giving up - failureThreshold: 6 - # -- number of consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - # -- endpoints of the control plane - endpoints: - # -- default api for health checks, should not be added to any ingress - default: - # -- port for incoming api calls - port: 8080 - # -- path for incoming api calls - path: /api - # -- data management api, used by internal users, can be added to an ingress and must not be internet facing - management: - # -- port for incoming api calls - port: 8081 - # -- path for incoming api calls - path: /management - # -- authentication key, must be attached to each 'X-Api-Key' request header - authKey: "" - # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not - control: - # -- port for incoming api calls - port: 8083 - # -- path for incoming api calls - path: /control - # -- ids api, used for inter connector communication and must be internet facing - protocol: - # -- port for incoming api calls - port: 8084 - # -- path for incoming api calls - path: /api/v1/ids - # -- metrics api, used for application metrics, must not be internet facing - metrics: - # -- port for incoming api calls - port: 9090 - # -- path for incoming api calls - path: /metrics - # -- observability api with unsecured access, must not be internet facing - observability: - # -- port for incoming API calls - port: 8085 - # -- observability api, provides /health /readiness and /liveness endpoints - path: /observability - # -- allow or disallow insecure access, i.e. access without authentication - insecure: true - service: - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. - type: ClusterIP - annotations: {} - # -- additional labels for the pod - podLabels: {} - # -- additional annotations for the pod - podAnnotations: {} - # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment - podSecurityContext: - seccompProfile: - # -- Restrict a Container's Syscalls with seccomp - type: RuntimeDefault - # -- Runs all processes within a pod with a special uid - runAsUser: 10001 - # -- Processes within a pod will belong to this guid - runAsGroup: 10001 - # -- The owner for volumes and any files created within volumes will belong to this guid - fsGroup: 10001 - # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod - securityContext: - capabilities: - # -- Specifies which capabilities to drop to reduce syscall attack surface - drop: - - ALL - # -- Specifies which capabilities to add to issue specialized syscalls - add: [] - # -- Whether the root filesystem is mounted in read-only mode - readOnlyRootFilesystem: true - # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID - allowPrivilegeEscalation: false - # -- Requires the container to run without root privileges - runAsNonRoot: true - # -- The container's process will run with the specified uid - runAsUser: 10001 - # Extra environment variables that will be pass onto deployment pods - env: {} - # ENV_NAME: value - - # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. - # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core - envValueFrom: {} - # ENV_NAME: - # configMapKeyRef: - # name: configmap-name - # key: value_key - # secretKeyRef: - # name: secret-name - # key: value_key - - # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from - envSecretNames: [] - # - first-secret - # - second-secret - - # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from - envConfigMapNames: [] - # - first-config-map - # - second-config-map - - ## Ingress declaration to expose the network service. - ingresses: - ## Public / Internet facing Ingress - - enabled: false - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-control.local" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - ids - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - ## Private / Intranet facing Ingress - - enabled: false - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-control.intranet" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - management - - control - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container - volumeMounts: [] - # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories - volumes: [] - # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container - resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - replicaCount: 1 - autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics - opentelemetry: |- - otel.javaagent.enabled=false - otel.javaagent.debug=false - # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) - logging: |- - .level=INFO - org.eclipse.edc.level=ALL - handlers=java.util.logging.ConsoleHandler - java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter - java.util.logging.ConsoleHandler.level=ALL - java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n - - # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes - nodeSelector: {} - # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes - tolerations: [] - # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on - affinity: {} - - url: - # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) - ids: "" - - dataplane: - image: - # -- Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically - repository: "" - # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use - pullPolicy: IfNotPresent - # -- Overrides the image tag whose default is the chart appVersion - tag: "" - initContainers: [] - debug: - enabled: false - port: 1044 - suspendOnStart: false - livenessProbe: - # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - # -- seconds to wait before performing the first liveness check - initialDelaySeconds: 30 - # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds - periodSeconds: 10 - # -- number of seconds after which the probe times out - timeoutSeconds: 5 - # -- when a probe fails kubernetes will try 6 times before giving up - failureThreshold: 6 - # -- number of consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - readinessProbe: - # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) - enabled: true - # -- seconds to wait before performing the first readiness check - initialDelaySeconds: 30 - # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds - periodSeconds: 10 - # -- number of seconds after which the probe times out - timeoutSeconds: 5 - # -- when a probe fails kubernetes will try 6 times before giving up - failureThreshold: 6 - # -- number of consecutive successes for the probe to be considered successful after having failed - successThreshold: 1 - service: - # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. - type: ClusterIP - port: 80 - endpoints: - default: - port: 8080 - path: /api - public: - port: 8081 - path: /api/public - control: - port: 8083 - path: /api/dataplane/control - observability: - # -- port for incoming API calls - port: 8085 - # -- observability api, provides /health /readiness and /liveness endpoints - path: /observability - # -- allow or disallow insecure access, i.e. access without authentication - insecure: true - metrics: - port: 9090 - path: /metrics - aws: - endpointOverride: "" - accessKeyId: "" - secretAccessKey: "" - # -- additional labels for the pod - podLabels: {} - # -- additional annotations for the pod - podAnnotations: {} - # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment - podSecurityContext: - seccompProfile: - # -- Restrict a Container's Syscalls with seccomp - type: RuntimeDefault - # -- Runs all processes within a pod with a special uid - runAsUser: 10001 - # -- Processes within a pod will belong to this guid - runAsGroup: 10001 - # -- The owner for volumes and any files created within volumes will belong to this guid - fsGroup: 10001 - # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod - securityContext: - capabilities: - # -- Specifies which capabilities to drop to reduce syscall attack surface - drop: - - ALL - # -- Specifies which capabilities to add to issue specialized syscalls - add: [] - # -- Whether the root filesystem is mounted in read-only mode - readOnlyRootFilesystem: true - # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID - allowPrivilegeEscalation: false - # -- Requires the container to run without root privileges - runAsNonRoot: true - # -- The container's process will run with the specified uid - runAsUser: 10001 - # Extra environment variables that will be pass onto deployment pods - env: {} - # ENV_NAME: value - - # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. - # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core - envValueFrom: {} - # ENV_NAME: - # configMapKeyRef: - # name: configmap-name - # key: value_key - # secretKeyRef: - # name: secret-name - # key: value_key - - # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from - envSecretNames: [] - # - first-secret - # - second-secret - - # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from - envConfigMapNames: [] - # - first-config-map - # - second-config-map - - ## Ingress declaration to expose the network service. - ingresses: - ## Public / Internet facing Ingress - - enabled: false - # -- The hostname to be used to precisely map incoming traffic onto the underlying network service - hostname: "edc-data.local" - # -- Additional ingress annotations to add - annotations: {} - # -- EDC endpoints exposed by this ingress resource - endpoints: - - public - # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use - className: "" - # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource - tls: - # -- Enables TLS on the ingress resource - enabled: false - # -- If present overwrites the default secret name - secretName: "" - ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource - certManager: - # -- If preset enables certificate generation via cert-manager namespace scoped issuer - issuer: "" - # -- If preset enables certificate generation via cert-manager cluster-wide issuer - clusterIssuer: "" - # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container - volumeMounts: [] - # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories - volumes: [] - # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container - resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - replicaCount: 1 - autoscaling: - # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) - enabled: false - # -- Minimal replicas if resource consumption falls below resource threshholds - minReplicas: 1 - # -- Maximum replicas if resource consumption exceeds resource threshholds - maxReplicas: 100 - # -- targetAverageUtilization of cpu provided to a pod - targetCPUUtilizationPercentage: 80 - # -- targetAverageUtilization of memory provided to a pod - targetMemoryUtilizationPercentage: 80 - # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics - opentelemetry: |- - otel.javaagent.enabled=false - otel.javaagent.debug=false - # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) - logging: |- - .level=INFO - org.eclipse.edc.level=ALL - handlers=java.util.logging.ConsoleHandler - java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter - java.util.logging.ConsoleHandler.level=ALL - java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n - # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes - nodeSelector: {} - # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes - tolerations: [] - # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on - affinity: {} - - url: - # -- Explicitly declared url for reaching the public api (e.g. if ingresses not used) - public: "" - - postgresql: - enabled: false - jdbcUrl: "" - username: "" - password: "" - - vault: - hashicorp: - enabled: true - url: "" - token: "" - timeout: 30 - healthCheck: - enabled: true - standbyOk: true - paths: - secret: /v1/secret - health: /v1/sys/health - secretNames: - transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key - transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key - transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key - dapsPrivateKey: daps-private-key - dapsPublicKey: daps-public-key - - daps: - url: "" - clientId: "" - paths: - jwks: /jwks.json - token: /token - - backendService: - httpProxyTokenReceiverUrl: "" - - serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) - imagePullSecrets: [] diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml index 71e9bf3cf..37be9015e 100644 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/Chart.yaml @@ -31,3 +31,15 @@ dependencies: repository: "file://../omejdn" alias: idsdaps condition: install.daps + + # HashiCorp Vault + - name: vault + alias: vault + version: 0.20.0 + repository: https://helm.releases.hashicorp.com + + # PostgreSQL + - name: postgresql + alias: postgresql + version: 12.1.6 + repository: https://charts.bitnami.com/bitnami \ No newline at end of file diff --git a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml index b9d7d5faf..9a48a1047 100644 --- a/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml +++ b/edc-tests/deployment/src/main/resources/helm/test-infrastructure/values.yaml @@ -1,10 +1,513 @@ --- -########### -# Install # -########### -install: - daps: true +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +fullnameOverride: "" +nameOverride: "" + +# -- Existing image pull secret to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) +imagePullSecrets: [] + +customLabels: {} + +runtime: + controlplane: + image: + # -- Which derivate of the control plane to use. when left empty the deployment will select the correct image automatically + repository: "" + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "" + initContainers: [] + debug: + enabled: false + port: 1044 + suspendOnStart: false + internationalDataSpaces: + id: TXDC + description: Tractus-X Eclipse IDS Data Space Connector + title: "" + maintainer: "" + curator: "" + catalogId: TXDC-Catalog + livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first liveness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + readinessProbe: + # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first readiness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a readiness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + # -- endpoints of the control plane + endpoints: + # -- default api for health checks, should not be added to any ingress + default: + # -- port for incoming api calls + port: 8080 + # -- path for incoming api calls + path: /api + # -- data management api, used by internal users, can be added to an ingress and must not be internet facing + management: + # -- port for incoming api calls + port: 8081 + # -- path for incoming api calls + path: /management + # -- authentication key, must be attached to each 'X-Api-Key' request header + authKey: "" + # -- control api, used for internal control calls. can be added to the internal ingress, but should probably not + control: + # -- port for incoming api calls + port: 8083 + # -- path for incoming api calls + path: /control + # -- ids api, used for inter connector communication and must be internet facing + protocol: + # -- port for incoming api calls + port: 8084 + # -- path for incoming api calls + path: /api/v1/ids + # -- metrics api, used for application metrics, must not be internet facing + metrics: + # -- port for incoming api calls + port: 9090 + # -- path for incoming api calls + path: /metrics + # -- observability api with unsecured access, must not be internet facing + observability: + # -- port for incoming API calls + port: 8085 + # -- observability api, provides /health /readiness and /liveness endpoints + path: /observability + # -- allow or disallow insecure access, i.e. access without authentication + insecure: true + service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + annotations: {} + # -- additional labels for the pod + podLabels: {} + # -- additional annotations for the pod + podAnnotations: {} + # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment + podSecurityContext: + seccompProfile: + # -- Restrict a Container's Syscalls with seccomp + type: RuntimeDefault + # -- Runs all processes within a pod with a special uid + runAsUser: 10001 + # -- Processes within a pod will belong to this guid + runAsGroup: 10001 + # -- The owner for volumes and any files created within volumes will belong to this guid + fsGroup: 10001 + # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod + securityContext: + capabilities: + # -- Specifies which capabilities to drop to reduce syscall attack surface + drop: + - ALL + # -- Specifies which capabilities to add to issue specialized syscalls + add: [] + # -- Whether the root filesystem is mounted in read-only mode + readOnlyRootFilesystem: true + # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID + allowPrivilegeEscalation: false + # -- Requires the container to run without root privileges + runAsNonRoot: true + # -- The container's process will run with the specified uid + runAsUser: 10001 + # Extra environment variables that will be pass onto deployment pods + env: {} + # ENV_NAME: value + + # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # secretKeyRef: + # name: secret-name + # key: value_key + + # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from + envSecretNames: [] + # - first-secret + # - second-secret + + # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from + envConfigMapNames: [] + # - first-config-map + # - second-config-map + + ## Ingress declaration to expose the network service. + ingresses: + ## Public / Internet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.local" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - ids + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + ## Private / Intranet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-control.intranet" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - management + - control + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container + volumeMounts: [] + # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories + volumes: [] + # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container + resources: + {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + replicaCount: 1 + autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics + opentelemetry: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) + logging: |- + .level=INFO + org.eclipse.edc.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n + + # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes + nodeSelector: {} + # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes + tolerations: [] + # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on + affinity: {} + + url: + # -- Explicitly declared url for reaching the ids api (e.g. if ingresses not used) + ids: "" + + dataplane: + image: + # -- Which derivate of the data plane to use. when left empty the deployment will select the correct image automatically + repository: "" + # -- [Kubernetes image pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) to use + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: "" + initContainers: [] + debug: + enabled: false + port: 1044 + suspendOnStart: false + livenessProbe: + # -- Whether to enable kubernetes [liveness-probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first liveness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + readinessProbe: + # -- Whether to enable kubernetes [readiness-probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + enabled: true + # -- seconds to wait before performing the first readiness check + initialDelaySeconds: 30 + # -- this fields specifies that kubernetes should perform a liveness check every 10 seconds + periodSeconds: 10 + # -- number of seconds after which the probe times out + timeoutSeconds: 5 + # -- when a probe fails kubernetes will try 6 times before giving up + failureThreshold: 6 + # -- number of consecutive successes for the probe to be considered successful after having failed + successThreshold: 1 + service: + # -- [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. + type: ClusterIP + port: 80 + endpoints: + default: + port: 8080 + path: /api + public: + port: 8081 + path: /api/public + control: + port: 8083 + path: /api/dataplane/control + observability: + # -- port for incoming API calls + port: 8085 + # -- observability api, provides /health /readiness and /liveness endpoints + path: /observability + # -- allow or disallow insecure access, i.e. access without authentication + insecure: true + metrics: + port: 9090 + path: /metrics + aws: + endpointOverride: "" + accessKeyId: "" + secretAccessKey: "" + # -- additional labels for the pod + podLabels: {} + # -- additional annotations for the pod + podAnnotations: {} + # -- The [pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) defines privilege and access control settings for a Pod within the deployment + podSecurityContext: + seccompProfile: + # -- Restrict a Container's Syscalls with seccomp + type: RuntimeDefault + # -- Runs all processes within a pod with a special uid + runAsUser: 10001 + # -- Processes within a pod will belong to this guid + runAsGroup: 10001 + # -- The owner for volumes and any files created within volumes will belong to this guid + fsGroup: 10001 + # The [container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container) defines privilege and access control settings for a Container within a pod + securityContext: + capabilities: + # -- Specifies which capabilities to drop to reduce syscall attack surface + drop: + - ALL + # -- Specifies which capabilities to add to issue specialized syscalls + add: [] + # -- Whether the root filesystem is mounted in read-only mode + readOnlyRootFilesystem: true + # -- Controls [Privilege Escalation](https://kubernetes.io/docs/concepts/security/pod-security-policy/#privilege-escalation) enabling setuid binaries changing the effective user ID + allowPrivilegeEscalation: false + # -- Requires the container to run without root privileges + runAsNonRoot: true + # -- The container's process will run with the specified uid + runAsUser: 10001 + # Extra environment variables that will be pass onto deployment pods + env: {} + # ENV_NAME: value + + # "valueFrom" environment variable references that will be added to deployment pods. Name is templated. + # ref: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvarsource-v1-core + envValueFrom: {} + # ENV_NAME: + # configMapKeyRef: + # name: configmap-name + # key: value_key + # secretKeyRef: + # name: secret-name + # key: value_key + + # [Kubernetes Secret Resource](https://kubernetes.io/docs/concepts/configuration/secret/) names to load environment variables from + envSecretNames: [] + # - first-secret + # - second-secret + + # [Kubernetes ConfigMap Resource](https://kubernetes.io/docs/concepts/configuration/configmap/) names to load environment variables from + envConfigMapNames: [] + # - first-config-map + # - second-config-map + + ## Ingress declaration to expose the network service. + ingresses: + ## Public / Internet facing Ingress + - enabled: false + # -- The hostname to be used to precisely map incoming traffic onto the underlying network service + hostname: "edc-data.local" + # -- Additional ingress annotations to add + annotations: {} + # -- EDC endpoints exposed by this ingress resource + endpoints: + - public + # -- Defines the [ingress class](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) to use + className: "" + # -- TLS [tls class](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) applied to the ingress resource + tls: + # -- Enables TLS on the ingress resource + enabled: false + # -- If present overwrites the default secret name + secretName: "" + ## Adds [cert-manager](https://cert-manager.io/docs/) annotations to the ingress resource + certManager: + # -- If preset enables certificate generation via cert-manager namespace scoped issuer + issuer: "" + # -- If preset enables certificate generation via cert-manager cluster-wide issuer + clusterIssuer: "" + # -- declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container + volumeMounts: [] + # -- [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories + volumes: [] + # -- [resource management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) for the container + resources: + {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + replicaCount: 1 + autoscaling: + # -- Enables [horizontal pod autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) + enabled: false + # -- Minimal replicas if resource consumption falls below resource threshholds + minReplicas: 1 + # -- Maximum replicas if resource consumption exceeds resource threshholds + maxReplicas: 100 + # -- targetAverageUtilization of cpu provided to a pod + targetCPUUtilizationPercentage: 80 + # -- targetAverageUtilization of memory provided to a pod + targetMemoryUtilizationPercentage: 80 + # -- configuration of the [Open Telemetry Agent](https://opentelemetry.io/docs/instrumentation/java/automatic/agent-config/) to collect and expose metrics + opentelemetry: |- + otel.javaagent.enabled=false + otel.javaagent.debug=false + # -- configuration of the [Java Util Logging Facade](https://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) + logging: |- + .level=INFO + org.eclipse.edc.level=ALL + handlers=java.util.logging.ConsoleHandler + java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter + java.util.logging.ConsoleHandler.level=ALL + java.util.logging.SimpleFormatter.format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS] [%4$-7s] %5$s%6$s%n + # [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) to constrain pods to nodes + nodeSelector: {} + # [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) to configure preferred nodes + tolerations: [] + # [affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) to configure which nodes the pods can be scheduled on + affinity: {} + + url: + # -- Explicitly declared url for reaching the public api (e.g. if ingresses not used) + public: "" + + postgresql: + enabled: false + jdbcUrl: "" + username: "" + password: "" + + vault: + hashicorp: + enabled: true + url: "" + token: "" + timeout: 30 + healthCheck: + enabled: true + standbyOk: true + paths: + secret: /v1/secret + health: /v1/sys/health + secretNames: + transferProxyTokenSignerPrivateKey: transfer-proxy-token-signer-private-key + transferProxyTokenSignerPublicKey: transfer-proxy-token-signer-public-key + transferProxyTokenEncryptionAesKey: transfer-proxy-token-encryption-aes-key + dapsPrivateKey: daps-private-key + dapsPublicKey: daps-public-key + + daps: + url: "" + clientId: "" + paths: + jwks: /jwks.json + token: /token + + backendService: + httpProxyTokenReceiverUrl: "" + + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + # -- Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) + imagePullSecrets: [] + ######## # DAPS # @@ -43,3 +546,104 @@ idsdaps: UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= -----END CERTIFICATE----- + + +############## +# POSTGRESQL # +############## +postgresql: + fullnameOverride: "postgresql" + primary: + persistence: + enabled: false + readReplicas: + persistence: + enabled: false + auth: + database: "edc" + username: "user" + password: "password" + +######### +# VAULT # +######### +vault: + fullnameOverride: "vault" + injector: + enabled: false + server: + dev: + enabled: true + devRootToken: "root" + # Must be the same certificate that is configured in section 'ids-daps' + postStart: + - "sh" + - "-c" + - | + { + + sleep 5 + + /bin/vault kv put secret/sokrates/data-encryption-aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== + + cat << EOF | /bin/vault kv put secret/daps-key content=- + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCv+NUvK7ppJPiM + wZPaQQxE745T5pV38O/Mkay5m82nnd5BoMoCdhhRTy3Efy79FhvBfGruFBLLGzsQ + FOEUY53Albeumo2gmpZSKjJR/M2ifK4MTaRniVOWL5mEcZSKPhsItKpxdLaiYfB6 + 8uzqkqNICtmAQRSclYKzLBM9xHLEtxDWCbnzYFCHoOELGi+PTNIFsUnsT3QuKaJ/ + ejb47vdA/EZbwCQdtTyJ6i54jGhZUp0WMwq1Go2uhzJsygPmT2da/ZZZc7BNNEQE + sUSMZSpMH807TG/TunstotrzO4ShhpV4zbJ2FV/VlxH7yuCawmnR84F/KnXs9fUc + RSrQfuYBAgMBAAECggEAO+KjsjTgcG3bhBNQnMLsSP15Y0Yicbn18ZlVvaivGS7Z + d14fwSytY+ZdPfTGaey/L16HCVSdfK9cr0Fbw9OO2P5ajzobnp9dLsMbctlkpbpm + hNtbarzKTF8QkIkSsuUl0BWjt46vpJ1N+Jl5VO7oUFkY4dPEDvG2lAEY3zlekWDm + cQeOC/YgpoW4xfRwPPS6QE0w3Q+H5NfNjfz+mSHeItTlVfTKDRliWQLPWeRZFuXh + FlRFUQnTmEE/9wpIe3Hn7WXJ3fQqcYDzxU7/zwwY9I7bB15SgVHlR0ENDPAD5X8F + MVZ3EcLlqGBy+WvTWALp6pc8YfhW3fiTWyuamXtNrQKBgQDonsIzBKEOOKdKGW0e + uyw79ErmnmzkY5nuMrMxrmTA4WKCfJ/YRRA+4sxiltWsIJ3UkHe3OBCSSCdj79hb + ugb/+UzE70hOdgrct2NUQqbrj3gvsVvU8ZRQgTRMqKpmC0zY7KOMx6NU85z3IvS1 + z5fjszcUv4kLQlldYGSAuqPy+wKBgQDBqIkc8p/wcw7ygo1q/GerNeszfoxiIFp8 + h4RWLVhkwrcXFz30wBlUWuv5/kxU8tmJcmXxe72EmUstd6wvNOAnYwCiile6zQiJ + vsr1axavZnGOtNGUp6DUAsd2iviBl7IZ7kAcqCrQo4ivGhfHmahH3hmg8wuAMjYB + 8f+FSPgaMwKBgQC7W4tMrjDOFIFhJEOIWfcRvvxI7VcFSNelS76aiDzsQVwnfxr7 + hPzFucQmsBgfUBHvMADMWGK4f1cCnh5kGtwidXgIsjVJxLeQ+EAPkLOCzQZfW3l8 + dKshgD9QcxTzpaxal5ZPAEikVqaZQtVYToCmzCTUGETYBbOWitnH+Qut2wKBgQC6 + Y6DcSLUhc0xOotLDxv1sbu/aVxF8nFEbDD+Vxf0Otc4MnmUWPRHj+8KlkVkcZcR0 + IrP1kThd+EDAGS+TG9wmbIY+6tH3S8HM+eJUBWcHGJ1xUZ1p61DC3Y3nDWiTKlLT + 3Fi+fCkBOHSku4Npq/2odh7Kp0JJd4o9oxJg0VNhuwKBgQDSFn7dqFE0Xmwc40Vr + 0wJH8cPWXKGt7KJENpj894buk2DniLD4w2x874dzTjrOFi6fKxEzbBNA9Rq9UPo8 + u9gKvl/IyWmV0c4zFCNMjRwVdnkMEte/lXcJZ67T4FXZByqAZlhrr/v0FD442Z9B + AjWFbUiBCFOo+gpAFcQGrkOQHA== + -----END PRIVATE KEY----- + EOF + + cat << EOF | /bin/vault kv put secret/daps-crt content=- + -----BEGIN CERTIFICATE----- + MIIEAzCCAuugAwIBAgIUXFgjbN7jxGRUDkoUvEwcN3zcew8wDQYJKoZIhvcNAQEL + BQAwgZAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl + cmxpbjEMMAoGA1UECgwDQk1XMSAwHgYDVQQLDBdlZGMtcGxheWdyb3VuZC1wYXJ0 + bmVyMTEvMC0GA1UEAwwmc29rcmF0ZXMtZWRjLmRlbW8uY2F0ZW5hLXgubmV0L0JQ + TjEyMzQwHhcNMjIwNTEwMDc1NzMzWhcNMjMwNTEwMDc1NzMzWjCBkDELMAkGA1UE + BhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVybGluMQwwCgYDVQQK + DANCTVcxIDAeBgNVBAsMF2VkYy1wbGF5Z3JvdW5kLXBhcnRuZXIxMS8wLQYDVQQD + DCZzb2tyYXRlcy1lZGMuZGVtby5jYXRlbmEteC5uZXQvQlBOMTIzNDCCASIwDQYJ + KoZIhvcNAQEBBQADggEPADCCAQoCggEBAK/41S8rumkk+IzBk9pBDETvjlPmlXfw + 78yRrLmbzaed3kGgygJ2GFFPLcR/Lv0WG8F8au4UEssbOxAU4RRjncCVt66ajaCa + llIqMlH8zaJ8rgxNpGeJU5YvmYRxlIo+Gwi0qnF0tqJh8Hry7OqSo0gK2YBBFJyV + grMsEz3EcsS3ENYJufNgUIeg4QsaL49M0gWxSexPdC4pon96Nvju90D8RlvAJB21 + PInqLniMaFlSnRYzCrUaja6HMmzKA+ZPZ1r9lllzsE00RASxRIxlKkwfzTtMb9O6 + ey2i2vM7hKGGlXjNsnYVX9WXEfvK4JrCadHzgX8qdez19RxFKtB+5gECAwEAAaNT + MFEwHQYDVR0OBBYEFOcHLXRWZjHwexDqtgMGTCN/7aZlMB8GA1UdIwQYMBaAFOcH + LXRWZjHwexDqtgMGTCN/7aZlMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL + BQADggEBAD2a5kuIdICNXfYLpSe7AIONwZVucaArYtpXBxHEy5lMJsTEJgjZzypd + iIMU7onEQGVbii6yVNpWfIpJYM4e8ytVdJuk5evclVKZs/lZ2IshLyWFVj+ITh2E + 28X4C/Hnmt4MPBCNowQf71nMp4LEziBgXp54qFV9C+qSTEVdrherRE0PU/zKyX10 + S/P5o42weTHnAO/pBN/8AmL3AymynKVgcPaW46IjjRAuc6kfZWCrYQ0M4+/7Ws5r + uM55Zae/L+C82OTNNaaK324ogsCkORPeQ23OCrRD8rZJmQ9bpoOGglPminfwEOhB + UHtyKgmvqCyOV3G/4G93W/xsLV0kxLA= + -----END CERTIFICATE----- + EOF + + /bin/vault kv put secret/aes-keys content=OcvxzWCK8ETSjt1jmZw3RA== + + } diff --git a/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml new file mode 100644 index 000000000..a1de36f32 --- /dev/null +++ b/edc-tests/deployment/src/main/resources/helm/tractusx-connector-test.yaml @@ -0,0 +1,82 @@ +# +# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +fullnameOverride: tx-prod + +################################ +# EDC ControlPlane + DataPlane # +################################ +controlplane: + service: + type: NodePort + endpoints: + management: + authKey: password + image: + pullPolicy: Never + tag: "latest" + repository: "edc-controlplane-postgresql-hashicorp-vault" + securityContext: + # avoids some errors in the log: cannot write temp files of large multipart requests when R/O + readOnlyRootFilesystem: false + +dataplane: + image: + pullPolicy: Never + tag: "latest" + repository: "edc-dataplane-hashicorp-vault" + + securityContext: + # avoids some errors in the log: cannot write temp files of large multipart requests when R/O + readOnlyRootFilesystem: false + + aws: + endpointOverride: http://minio:9000 + secretAccessKey: qwerty123 + accessKeyId: qwerty123 + +postgresql: + username: user + password: password + jdbcUrl: jdbc:postgresql://postgresql:5432/edc + +vault: + hashicorp: + url: http://vault:8200 + token: root + + secretNames: + transferProxyTokenSignerPublicKey: daps-crt + transferProxyTokenSignerPrivateKey: daps-key + transferProxyTokenEncryptionAesKey: aes-keys + dapsPrivateKey: daps-key + dapsPublicKey: daps-crt + + # this must be set through CLI args: --set vault.secrets=$YOUR_VAULT_SECRETS where YOUR_VAULT_SECRETS should + # be a string in the format "key1:secret1;key2:secret2;..." + secrets: + +daps: + url: "http://ids-daps:4567" + clientId: "E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65:keyid:E7:07:2D:74:56:66:31:F0:7B:10:EA:B6:03:06:4C:23:7F:ED:A6:65" + +backendService: + httpProxyTokenReceiverUrl: "http://backend:8080" + From c5cd8b58690a8da334e8bc34c3cccc26a7c9103e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 May 2023 15:22:56 +0200 Subject: [PATCH 127/132] chore(deps): bump io.cucumber:cucumber-junit-platform-engine (#300) Bumps [io.cucumber:cucumber-junit-platform-engine](https://github.com/cucumber/cucumber-jvm) from 7.11.2 to 7.12.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.11.2...v7.12.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-junit-platform-engine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-tests/cucumber/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index 5d80a8a6e..010003956 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -20,7 +20,7 @@ dependencies { testImplementation("org.apache.httpcomponents:httpclient:4.5.14") testImplementation("org.junit.platform:junit-platform-suite:1.9.3") testImplementation("io.cucumber:cucumber-java:7.11.2") - testImplementation("io.cucumber:cucumber-junit-platform-engine:7.11.2") + testImplementation("io.cucumber:cucumber-junit-platform-engine:7.12.0") testImplementation("org.slf4j:slf4j-api:2.0.7") testImplementation(libs.restAssured) testImplementation(libs.postgres) From 6b18d624392938db70282cb8bcc21b1273a18aae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 May 2023 15:41:03 +0200 Subject: [PATCH 128/132] chore(deps): bump io.cucumber:cucumber-java from 7.11.2 to 7.12.0 (#301) Bumps [io.cucumber:cucumber-java](https://github.com/cucumber/cucumber-jvm) from 7.11.2 to 7.12.0. - [Release notes](https://github.com/cucumber/cucumber-jvm/releases) - [Changelog](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-jvm/compare/v7.11.2...v7.12.0) --- updated-dependencies: - dependency-name: io.cucumber:cucumber-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- edc-tests/cucumber/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edc-tests/cucumber/build.gradle.kts b/edc-tests/cucumber/build.gradle.kts index 010003956..e276b6442 100644 --- a/edc-tests/cucumber/build.gradle.kts +++ b/edc-tests/cucumber/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { testImplementation("com.google.code.gson:gson:2.10.1") testImplementation("org.apache.httpcomponents:httpclient:4.5.14") testImplementation("org.junit.platform:junit-platform-suite:1.9.3") - testImplementation("io.cucumber:cucumber-java:7.11.2") + testImplementation("io.cucumber:cucumber-java:7.12.0") testImplementation("io.cucumber:cucumber-junit-platform-engine:7.12.0") testImplementation("org.slf4j:slf4j-api:2.0.7") testImplementation(libs.restAssured) From 377a6d01813b5ee1c4c9af48972f5ec347e1d357 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Wed, 3 May 2023 18:29:34 +0200 Subject: [PATCH 129/132] chore: remove all printstacktrace statementsw (#304) --- .../objectstore/ObjectStoreServiceInMemory.java | 5 +++-- .../service/objectstore/ObjectStoreServiceSql.java | 4 ++-- .../tractusx/edc/cp/adapter/store/SqlObjectStore.java | 8 ++++---- .../tractusx/edc/cp/adapter/store/SqlQueueStore.java | 10 +++++----- .../edc/cp/adapter/service/ResultServiceTest.java | 2 +- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/service/objectstore/ObjectStoreServiceInMemory.java b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/service/objectstore/ObjectStoreServiceInMemory.java index 3e1a8190a..ccfd6e44d 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/service/objectstore/ObjectStoreServiceInMemory.java +++ b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/service/objectstore/ObjectStoreServiceInMemory.java @@ -22,6 +22,7 @@ import java.util.Objects; import java.util.stream.Collectors; import lombok.AllArgsConstructor; +import org.eclipse.edc.spi.EdcException; @AllArgsConstructor public class ObjectStoreServiceInMemory implements ObjectStoreService { @@ -33,7 +34,7 @@ public void put(String key, ObjectType objectType, Object object) { try { map.put(getKey(key, objectType), mapper.writeValueAsString(object)); } catch (JsonProcessingException e) { - e.printStackTrace(); + throw new IllegalArgumentException(); } } @@ -67,7 +68,7 @@ private T map(Class type, String json) { try { object = mapper.readValue(json, type); } catch (JsonProcessingException e) { - e.printStackTrace(); + throw new EdcException(e); } return object; } diff --git a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/service/objectstore/ObjectStoreServiceSql.java b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/service/objectstore/ObjectStoreServiceSql.java index b16daea50..1fb528e2c 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/service/objectstore/ObjectStoreServiceSql.java +++ b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/service/objectstore/ObjectStoreServiceSql.java @@ -70,7 +70,7 @@ private String objectToJson(Object object, String type) { try { return mapper.writeValueAsString(object); } catch (JsonProcessingException e) { - e.printStackTrace(); + throw new IllegalArgumentException(String.format("Can not parse object of type %s", type)); } } @@ -79,7 +79,7 @@ private T jsonToObject(ObjectEntity entity, Class type) { try { return mapper.readValue(entity.getObject(), type); } catch (JsonProcessingException e) { - e.printStackTrace(); + throw new IllegalArgumentException(String.format("Can not parse object of type %s", type)); } } diff --git a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/store/SqlObjectStore.java b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/store/SqlObjectStore.java index 4a5ec94a9..5540428f3 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/store/SqlObjectStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/store/SqlObjectStore.java @@ -58,7 +58,7 @@ public void saveMessage(ObjectEntity objectEntity) { objectEntity.getType(), objectEntity.getObject()); } catch (SQLException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } }); @@ -71,7 +71,7 @@ public ObjectEntity find(String id, String type) { var sql = statements.getFindByIdAndTypeTemplate(); return executeQuerySingle(connection, false, this::mapObjectEntity, sql, id, type); } catch (SQLException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } }); @@ -88,7 +88,7 @@ public List find(String type) { stream.close(); return result; } catch (SQLException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } }); @@ -101,7 +101,7 @@ public void deleteMessage(String id, String type) { var stmt = statements.getDeleteTemplate(); executeQuery(connection, stmt, id, type); } catch (SQLException | IllegalStateException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } }); diff --git a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/store/SqlQueueStore.java b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/store/SqlQueueStore.java index 0d808fae1..60336eb5b 100644 --- a/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/store/SqlQueueStore.java +++ b/edc-extensions/control-plane-adapter/src/main/java/org/eclipse/tractusx/edc/cp/adapter/store/SqlQueueStore.java @@ -69,7 +69,7 @@ public void saveMessage(QueueMessage queueMessage) { toJson(queueMessage.getMessage()), queueMessage.getInvokeAfter()); } catch (SQLException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } }); @@ -82,7 +82,7 @@ public QueueMessage findById(String id) { var sql = statements.getFindByIdTemplate(); return executeQuerySingle(connection, false, this::mapQueueMessage, sql, id); } catch (SQLException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } }); @@ -99,7 +99,7 @@ public void deleteMessage(String id) { var stmt = statements.getDeleteTemplate(); executeQuery(connection, stmt, id); } catch (SQLException | IllegalStateException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } } @@ -123,7 +123,7 @@ public void updateMessage(QueueMessage queueMessage) { queueMessage.getInvokeAfter(), queueMessage.getId()); } catch (SQLException | IllegalStateException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } } @@ -146,7 +146,7 @@ public List findMessagesToSend(int max) { stream.close(); return result; } catch (SQLException e) { - e.printStackTrace(); + throw new EdcPersistenceException(e); } }); diff --git a/edc-extensions/control-plane-adapter/src/test/java/org/eclipse/tractusx/edc/cp/adapter/service/ResultServiceTest.java b/edc-extensions/control-plane-adapter/src/test/java/org/eclipse/tractusx/edc/cp/adapter/service/ResultServiceTest.java index 2b7bff25f..0c4c649de 100644 --- a/edc-extensions/control-plane-adapter/src/test/java/org/eclipse/tractusx/edc/cp/adapter/service/ResultServiceTest.java +++ b/edc-extensions/control-plane-adapter/src/test/java/org/eclipse/tractusx/edc/cp/adapter/service/ResultServiceTest.java @@ -119,7 +119,7 @@ private void sleep(long milisec) { try { Thread.sleep(milisec); } catch (InterruptedException e) { - e.printStackTrace(); + throw new RuntimeException(e); } } From d4cab421693b1cc66d1f393eff0c7b8a79773eae Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 4 May 2023 11:57:42 +0200 Subject: [PATCH 130/132] chore(build): use tractusx bot creds for release PRs etc. --- .github/workflows/draft-new-release.yaml | 4 ++-- .github/workflows/helm-chart-release.yaml | 4 ++-- .github/workflows/publish-new-release.yml | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/draft-new-release.yaml b/.github/workflows/draft-new-release.yaml index 9ed3e9634..810523fee 100644 --- a/.github/workflows/draft-new-release.yaml +++ b/.github/workflows/draft-new-release.yaml @@ -30,8 +30,8 @@ jobs: - name: Initialize mandatory git config run: | - git config user.name "GitHub actions" - git config user.email noreply@github.com + git config user.name "eclipse-tractusx-bot" + git config user.email "tractusx-bot@eclipse.org" - uses: ./.github/actions/setup-java - diff --git a/.github/workflows/helm-chart-release.yaml b/.github/workflows/helm-chart-release.yaml index 40aeecc6d..0e282826f 100644 --- a/.github/workflows/helm-chart-release.yaml +++ b/.github/workflows/helm-chart-release.yaml @@ -44,8 +44,8 @@ jobs: - name: Configure Git run: | - git config user.name "$GITHUB_ACTOR" - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + git config user.name "eclipse-tractusx-bot" + git config user.email "tractusx-bot@eclipse.org" - name: Install Helm uses: azure/setup-helm@v3 with: diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml index 491af119b..85e5e27a4 100644 --- a/.github/workflows/publish-new-release.yml +++ b/.github/workflows/publish-new-release.yml @@ -131,8 +131,8 @@ jobs: name: Package helm, update index.yaml and push to gh-pages run: | # Prepare git env - git config user.name "GitHub actions" - git config user.email noreply@github.com + git config user.name "eclipse-tractusx-bot" + git config user.email "tractusx-bot@eclipse.org" # Package all charts find charts -name Chart.yaml -not -path "./edc-tests/*" | xargs -n1 dirname | xargs -n1 helm package -u -d helm-charts @@ -175,8 +175,8 @@ jobs: id: create_release_tag run: | # Prepare git env - git config user.name "$GITHUB_ACTOR" - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + git config user.name "eclipse-tractusx-bot" + git config user.email "tractusx-bot@eclipse.org" # informative git branch -a @@ -204,8 +204,8 @@ jobs: if: github.event.pull_request.base.ref == 'releases' run: | # Prepare git env - git config user.name "GitHub actions" - git config user.email noreply@github.com + git config user.name "eclipse-tractusx-bot" + git config user.email "tractusx-bot@eclipse.org" # Merge releases into main git checkout main && git merge -X theirs releases --no-commit --no-ff From 96debfcc5492c13e5f6f40beebebf8e31791969d Mon Sep 17 00:00:00 2001 From: eclipse-tractusx-bot Date: Thu, 4 May 2023 10:01:25 +0000 Subject: [PATCH 131/132] Prepare release 0.3.4 --- CHANGELOG.md | 322 ++++++++++---------- charts/tractusx-connector-memory/Chart.yaml | 4 +- charts/tractusx-connector-memory/README.md | 4 +- charts/tractusx-connector/Chart.yaml | 4 +- charts/tractusx-connector/README.md | 9 +- gradle.properties | 2 +- 6 files changed, 173 insertions(+), 172 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 316589f06..113654b98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,29 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.3.4] - 2023-05-04 + ## [0.3.3] - 2023-04-28 ### Fixed -- Added license headers to several files in the code base -- Refactoring of Helm charts - multiple charts instead of one dynamically assembled chart +- Added license headers to several files in the code base +- Refactoring of Helm charts - multiple charts instead of one dynamically assembled chart ## [0.3.3] - 2023-04-19 ### Fixed -- Config values for the data plane part of the helm chart -- Contract Validity +- Config values for the data plane part of the helm chart +- Contract Validity ### Added -- A log line whenever a policy evaluation of the BPN number was performed +- A log line whenever a policy evaluation of the BPN number was performed ## [0.3.2] - 2023-03-30 ### Fixed -- Fixed mutually-exclusive config values for Azure KeyVault +- Fixed mutually-exclusive config values for Azure KeyVault ## [0.3.1] - 2023-03-27 @@ -37,7 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Support unauthenticated access to the ObservabilityAPI (#126) +- Support unauthenticated access to the ObservabilityAPI (#126) ### Fixed @@ -48,144 +50,144 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). ### Added -- Add contract id to data source http call (#732) -- Support also support releases in ci pipeline -- Introduce typed object for oauth2 provisioning -- Add documentation -- Add test case -- Add client to omejdn -- add hydra deployment -- Configure dynamically HTTP Receiver callback endpoints. (#685) -- cp-adapter : code review, rollbacke name change (#664) -- Feature/cp adapter task 355 356 357 (#621) -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Local TXDC Setup Documentation (#618) -- Feature: Sftp Provisioner and Client (#554) +- Add contract id to data source http call (#732) +- Support also support releases in ci pipeline +- Introduce typed object for oauth2 provisioning +- Add documentation +- Add test case +- Add client to omejdn +- add hydra deployment +- Configure dynamically HTTP Receiver callback endpoints. (#685) +- cp-adapter : code review, rollbacke name change (#664) +- Feature/cp adapter task 355 356 357 (#621) +- Add Validity Mapping in ContractDefinitionStepDefs class +- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps +- Add validity attribute in class ContractDefinition +- Add Validity Mapping in ContractDefinitionStepDefs class +- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps +- Add validity attribute in class ContractDefinition +- Local TXDC Setup Documentation (#618) +- Feature: Sftp Provisioner and Client (#554) ### Changed -- Support horizontal edc scaling in cp adapter extension (#678) -- Use upstream jackson version (#741) -- Replace provision-oauth2 with data-plane-http-oauth2 -- docs: Update sample documentation (#671) -- chore: Disable build ci pipeline if just docu was updated (#705) -- Increase trivy timeout -- Remove not useful anymore custom-jsonld extension (#683) -- update setup docu (#654) -- remove trailing slash (#652) -- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) -- Feature/set charts deprecated (#628) -- update setup docu (#627) -- Feature/update txdc deployment downward capabilities (#625) -- remove git submodule (#619) -- Feature/update postman (#624) -- update control plane docu (#623) -- update postgresql version in Chart.yaml supporting-infrastructure (#622) -- update link to edc logo in README.md (#612) -- update description of supporting infrastructure deployment (#616) +- Support horizontal edc scaling in cp adapter extension (#678) +- Use upstream jackson version (#741) +- Replace provision-oauth2 with data-plane-http-oauth2 +- docs: Update sample documentation (#671) +- chore: Disable build ci pipeline if just docu was updated (#705) +- Increase trivy timeout +- Remove not useful anymore custom-jsonld extension (#683) +- update setup docu (#654) +- remove trailing slash (#652) +- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) +- Feature/set charts deprecated (#628) +- update setup docu (#627) +- Feature/update txdc deployment downward capabilities (#625) +- remove git submodule (#619) +- Feature/update postman (#624) +- update control plane docu (#623) +- update postgresql version in Chart.yaml supporting-infrastructure (#622) +- update link to edc logo in README.md (#612) +- update description of supporting infrastructure deployment (#616) ### Fixed -- bugfix: Fix slow AES encryption (#746) -- Fix typo in tractusx-connector values.yaml comment -- Fix not working docu link in README.md -- Fix typo in control-plane adapter README +- bugfix: Fix slow AES encryption (#746) +- Fix typo in tractusx-connector values.yaml comment +- Fix not working docu link in README.md +- Fix typo in control-plane adapter README ### Dependency updates -- Bump EDC to 20220220 (#767) -- Bump alpine (#749) -- Bump alpine (#750) -- Bump alpine (#752) -- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#753) -- Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) -- Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) -- Bump s3 from 2.19.33 to 2.20.0 -- Bump s3 from 2.19.27 to 2.19.33 -- Bump jaxb-runtime from 4.0.1 to 4.0.2 -- Bump spotless-maven-plugin from 2.31.0 to 2.32.0 -- Bump postgresql from 42.5.1 to 42.5.3 -- Bump nimbus-jose-jwt from 9.30 to 9.30.1 -- Bump lombok from 1.18.24 to 1.18.26 -- Bump flyway-core from 9.12.0 to 9.14.1 -- Bump jackson-bom from 2.14.0-rc2 to 2.14.2 -- Bump cucumber.version from 7.11.0 to 7.11.1 -- Bump azure-sdk-bom from 1.2.8 to 1.2.9 -- Bump mockito-bom from 5.0.0 to 5.1.1 -- Bump edc version to 0.0.1-20230131-SNAPSHOT -- Bump s3 from 2.19.18 to 2.19.27 -- Bump docker/build-push-action from 3 to 4 -- Bump nimbus-jose-jwt from 9.29 to 9.30 -- Bump spotless-maven-plugin from 2.30.0 to 2.31.0 -- Bump nimbus-jose-jwt from 9.28 to 9.29 -- Bump mockito-bom from 4.11.0 to 5.0.0 -- Bump edc version to 0.0.1-20230125-SNAPSHOT -- Bump flyway-core from 9.11.0 to 9.12.0 -- Bump s3 from 2.19.15 to 2.19.18 (#684) -- Bump mikefarah/yq from 4.30.6 to 4.30.8 (#682) -- Bump spotless-maven-plugin from 2.29.0 to 2.30.0 -- Bump edc version to 0.0.1-20230115-SNAPSHOT -- Bump cucumber.version from 7.10.1 to 7.11.0 (#672) -- Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#669) -- Bump s3 from 2.19.11 to 2.19.15 (#668) -- Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) -- Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) -- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#659) -- Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) -- Bump alpine (#658) -- Bump alpine (#661) -- Bump alpine (#662) -- Bump azure/setup-kubectl from 3.1 to 3.2 (#655) -- Bump junit-bom from 5.9.1 to 5.9.2 (#657) -- Bump s3 from 2.19.2 to 2.19.11 (#648) -- Bump actions/checkout from 3.2.0 to 3.3.0 (#647) -- Bump flyway-core from 9.10.2 to 9.11.0 (#646) -- Bump spotless-maven-plugin from 2.28.0 to 2.29.0 (#641) -- Bump mockito-bom from 4.10.0 to 4.11.0 (#637) -- Bump flyway-core from 9.10.1 to 9.10.2 (#632) -- Bump s3 from 2.19.1 to 2.19.2 (#631) -- Bump s3 from 2.18.41 to 2.19.1 (#626) -- Bump mikefarah/yq from 4.30.5 to 4.30.6 (#613) -- Bump cucumber.version from 7.10.0 to 7.10.1 (#614) -- Bump s3 from 2.18.40 to 2.18.41 (#615) -- Bump azure/setup-helm from 3.4 to 3.5 (#596) -- Bump actions/checkout from 3.1.0 to 3.2.0 (#598) -- Bump mockito-bom from 4.9.0 to 4.10.0 (#607) -- Bump s3 from 2.18.39 to 2.18.40 (#609) -- Bump flyway-core from 9.10.0 to 9.10.1 (#610) -- Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) -- Bump s3 from 2.18.35 to 2.18.39 (#606) +- Bump EDC to 20220220 (#767) +- Bump alpine (#749) +- Bump alpine (#750) +- Bump alpine (#752) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#753) +- Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) +- Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) +- Bump s3 from 2.19.33 to 2.20.0 +- Bump s3 from 2.19.27 to 2.19.33 +- Bump jaxb-runtime from 4.0.1 to 4.0.2 +- Bump spotless-maven-plugin from 2.31.0 to 2.32.0 +- Bump postgresql from 42.5.1 to 42.5.3 +- Bump nimbus-jose-jwt from 9.30 to 9.30.1 +- Bump lombok from 1.18.24 to 1.18.26 +- Bump flyway-core from 9.12.0 to 9.14.1 +- Bump jackson-bom from 2.14.0-rc2 to 2.14.2 +- Bump cucumber.version from 7.11.0 to 7.11.1 +- Bump azure-sdk-bom from 1.2.8 to 1.2.9 +- Bump mockito-bom from 5.0.0 to 5.1.1 +- Bump edc version to 0.0.1-20230131-SNAPSHOT +- Bump s3 from 2.19.18 to 2.19.27 +- Bump docker/build-push-action from 3 to 4 +- Bump nimbus-jose-jwt from 9.29 to 9.30 +- Bump spotless-maven-plugin from 2.30.0 to 2.31.0 +- Bump nimbus-jose-jwt from 9.28 to 9.29 +- Bump mockito-bom from 4.11.0 to 5.0.0 +- Bump edc version to 0.0.1-20230125-SNAPSHOT +- Bump flyway-core from 9.11.0 to 9.12.0 +- Bump s3 from 2.19.15 to 2.19.18 (#684) +- Bump mikefarah/yq from 4.30.6 to 4.30.8 (#682) +- Bump spotless-maven-plugin from 2.29.0 to 2.30.0 +- Bump edc version to 0.0.1-20230115-SNAPSHOT +- Bump cucumber.version from 7.10.1 to 7.11.0 (#672) +- Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#669) +- Bump s3 from 2.19.11 to 2.19.15 (#668) +- Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) +- Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#659) +- Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) +- Bump alpine (#658) +- Bump alpine (#661) +- Bump alpine (#662) +- Bump azure/setup-kubectl from 3.1 to 3.2 (#655) +- Bump junit-bom from 5.9.1 to 5.9.2 (#657) +- Bump s3 from 2.19.2 to 2.19.11 (#648) +- Bump actions/checkout from 3.2.0 to 3.3.0 (#647) +- Bump flyway-core from 9.10.2 to 9.11.0 (#646) +- Bump spotless-maven-plugin from 2.28.0 to 2.29.0 (#641) +- Bump mockito-bom from 4.10.0 to 4.11.0 (#637) +- Bump flyway-core from 9.10.1 to 9.10.2 (#632) +- Bump s3 from 2.19.1 to 2.19.2 (#631) +- Bump s3 from 2.18.41 to 2.19.1 (#626) +- Bump mikefarah/yq from 4.30.5 to 4.30.6 (#613) +- Bump cucumber.version from 7.10.0 to 7.10.1 (#614) +- Bump s3 from 2.18.40 to 2.18.41 (#615) +- Bump azure/setup-helm from 3.4 to 3.5 (#596) +- Bump actions/checkout from 3.1.0 to 3.2.0 (#598) +- Bump mockito-bom from 4.9.0 to 4.10.0 (#607) +- Bump s3 from 2.18.39 to 2.18.40 (#609) +- Bump flyway-core from 9.10.0 to 9.10.1 (#610) +- Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) +- Bump s3 from 2.18.35 to 2.18.39 (#606) ## [0.1.6] - 2023-02-20 ### Fixed -- SQL leakage issue -- Catalog pagination +- SQL leakage issue +- Catalog pagination ## [0.1.5] - 2023-02-13 ### Fixed -- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug -- Data Encryption extension: fixed usage of a blocking algorithm +- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug +- Data Encryption extension: fixed usage of a blocking algorithm ## [0.1.2] - 2022-09-30 ### Added -- Introduced DEPENDENCIES file +- Introduced DEPENDENCIES file ### Changed -- Moved helm charts from `deployment/helm` to `charts` -- Replaced distroless image with alpine in all docker images -- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` +- Moved helm charts from `deployment/helm` to `charts` +- Replaced distroless image with alpine in all docker images +- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` ## [0.1.1] - 2022-09-04 @@ -194,17 +196,17 @@ connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). ### Added -- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) +- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) ### Changed -- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) -- Helm Charts: TLS secret name is now configurable +- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) +- Helm Charts: TLS secret name is now configurable ### Fixed -- Connectors with Azure Vault extension are now starting - again [link](https://github.com/eclipse-edc/Connector/issues/1892) +- Connectors with Azure Vault extension are now starting + again [link](https://github.com/eclipse-edc/Connector/issues/1892) ## [0.1.0] - 2022-08-19 @@ -213,74 +215,74 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Added -- Control-Plane - extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) - - run the EDC with multiple data planes at once -- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) - - add data plane instances to the control plane by configuration -- Data-Plane - extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) - - transfer from and to AWS S3 buckets -- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) - - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) +- Control-Plane + extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) + - run the EDC with multiple data planes at once +- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) + - add data plane instances to the control plane by configuration +- Data-Plane + extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) + - transfer from and to AWS S3 buckets +- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) + - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) ### Changed -- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) -- EDC has been updated to - version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - - implications to the behavior of the connector have been covered in - the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) +- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) +- EDC has been updated to + version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - + implications to the behavior of the connector have been covered in + the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) ### Fixed -- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving - offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) -- Deletion of Asset becomes impossible when Contract Negotiation - exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) -- Deletion of Policy becomes impossible when Contract Definition - exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving + offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) +- Deletion of Asset becomes impossible when Contract Negotiation + exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) +- Deletion of Policy becomes impossible when Contract Definition + exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) ## [0.0.6] - 2022-07-29 ### Fixed -- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath - issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) - library +- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath + issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) + library ## [0.0.5] - 2022-07-28 ### Added -- EDC Health Checks for HashiCorp Vault +- EDC Health Checks for HashiCorp Vault ### Changed -- BusinessPartnerNumber constraint supports List structure -- Helm: Confidential EDC settings can be set using k8s secrets -- HashiCorp Vault API path configurable +- BusinessPartnerNumber constraint supports List structure +- Helm: Confidential EDC settings can be set using k8s secrets +- HashiCorp Vault API path configurable ## [0.0.4] - 2022-06-27 ### Added -- HashiCorp Vault Extension -- Control Plane with HashiCorp Vault and PostgreSQL support +- HashiCorp Vault Extension +- Control Plane with HashiCorp Vault and PostgreSQL support ### Changed -- Release Workflow now publishes EDC Extensions as Maven Artifacts +- Release Workflow now publishes EDC Extensions as Maven Artifacts ### Fixed -- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 - contract offers max. +- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 + contract offers max. ### Removed -- CosmosDB Control Plane -- Control API Extension for all Control Planes +- CosmosDB Control Plane +- Control API Extension for all Control Planes ## [0.0.3] - 2022-05-23 @@ -288,7 +290,9 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ## [0.0.1] - 2022-05-13 -[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.3...HEAD +[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.4...HEAD + +[0.3.4]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.3...0.3.4 [0.3.3]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.2...0.3.3 diff --git a/charts/tractusx-connector-memory/Chart.yaml b/charts/tractusx-connector-memory/Chart.yaml index cb0a06b72..e16443b8f 100644 --- a/charts/tractusx-connector-memory/Chart.yaml +++ b/charts/tractusx-connector-memory/Chart.yaml @@ -34,12 +34,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.3 +version: 0.3.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.3.3" +appVersion: "0.3.4" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector-memory diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md index 872827664..a3264b3e3 100644 --- a/charts/tractusx-connector-memory/README.md +++ b/charts/tractusx-connector-memory/README.md @@ -1,6 +1,6 @@ # tractusx-connector-memory -![Version: 0.3.3](https://img.shields.io/badge/Version-0.3.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.3](https://img.shields.io/badge/AppVersion-0.3.3-informational?style=flat-square) +![Version: 0.3.4](https://img.shields.io/badge/Version-0.3.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.4](https://img.shields.io/badge/AppVersion-0.3.4-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector based on memory @@ -10,7 +10,7 @@ A Helm chart for Tractus-X Eclipse Data Space Connector based on memory ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector-memory --version 0.3.3 +helm install my-release tractusx-edc/tractusx-connector-memory --version 0.3.4 ``` ## Source Code diff --git a/charts/tractusx-connector/Chart.yaml b/charts/tractusx-connector/Chart.yaml index 8bac9b082..b13ff5d01 100644 --- a/charts/tractusx-connector/Chart.yaml +++ b/charts/tractusx-connector/Chart.yaml @@ -40,12 +40,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.3.3 +version: 0.3.4 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.3.3" +appVersion: "0.3.4" home: https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector sources: - https://github.com/eclipse-tractusx/tractusx-edc/tree/main/charts/tractusx-connector diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index d4f89c56e..18fc67024 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -1,9 +1,9 @@ # tractusx-connector -![Version: 0.3.3](https://img.shields.io/badge/Version-0.3.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.3](https://img.shields.io/badge/AppVersion-0.3.3-informational?style=flat-square) +![Version: 0.3.4](https://img.shields.io/badge/Version-0.3.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.3.4](https://img.shields.io/badge/AppVersion-0.3.4-informational?style=flat-square) A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a -Control Plane and a Data Plane. Note that no external dependencies such as a PostgreSQL database and HashiCorp Vault are included. +Control Plane and a Data Plane. Note that _no_ external dependencies such as a PostgreSQL database and HashiCorp Vault are included. This chart is intended for use with an _existing_ PostgreSQL database and an _existing_ HashiCorp Vault. @@ -13,7 +13,7 @@ This chart is intended for use with an _existing_ PostgreSQL database and an _ex ```shell helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev -helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 +helm install my-release tractusx-edc/tractusx-connector --version 0.3.4 ``` ## Source Code @@ -124,7 +124,6 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 | controlplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | controlplane.tolerations | list | `[]` | | | controlplane.url.ids | string | `""` | Explicitly declared url for reaching the ids api (e.g. if ingresses not used) | -| controlplane.url.readiness | string | `""` | | | controlplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | | controlplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | | customLabels | object | `{}` | | @@ -207,7 +206,6 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 | dataplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | dataplane.tolerations | list | `[]` | | | dataplane.url.public | string | `""` | Explicitly declared url for reaching the public api (e.g. if ingresses not used) | -| dataplane.url.readiness | string | `""` | | | dataplane.volumeMounts | list | `[]` | declare where to mount [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) into the container | | dataplane.volumes | list | `[]` | [volume](https://kubernetes.io/docs/concepts/storage/volumes/) directories | | fullnameOverride | string | `""` | | @@ -221,7 +219,6 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.3.3 | serviceAccount.create | bool | `true` | | | serviceAccount.imagePullSecrets | list | `[]` | Existing image pull secret bound to the service account to use to [obtain the container image from private registries](https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry) | | serviceAccount.name | string | `""` | | -| vault.hashicorp.enabled | bool | `true` | | | vault.hashicorp.healthCheck.enabled | bool | `true` | | | vault.hashicorp.healthCheck.standbyOk | bool | `true` | | | vault.hashicorp.paths.health | string | `"/v1/sys/health"` | | diff --git a/gradle.properties b/gradle.properties index 6b3bcce1e..6f20531b8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ groupId=org.eclipse.tractusx.edc -version=0.3.4-SNAPSHOT +version=0.3.4 javaVersion=11 # configure the build: From 4f457ddad8c25e3a537e42a374251c4f60f1c301 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 4 May 2023 12:03:32 +0200 Subject: [PATCH 132/132] fixed CHANGELOG --- CHANGELOG.md | 322 +++++++++++++++++++++++++-------------------------- 1 file changed, 159 insertions(+), 163 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 113654b98..316589f06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,31 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.3.4] - 2023-05-04 - ## [0.3.3] - 2023-04-28 ### Fixed -- Added license headers to several files in the code base -- Refactoring of Helm charts - multiple charts instead of one dynamically assembled chart +- Added license headers to several files in the code base +- Refactoring of Helm charts - multiple charts instead of one dynamically assembled chart ## [0.3.3] - 2023-04-19 ### Fixed -- Config values for the data plane part of the helm chart -- Contract Validity +- Config values for the data plane part of the helm chart +- Contract Validity ### Added -- A log line whenever a policy evaluation of the BPN number was performed +- A log line whenever a policy evaluation of the BPN number was performed ## [0.3.2] - 2023-03-30 ### Fixed -- Fixed mutually-exclusive config values for Azure KeyVault +- Fixed mutually-exclusive config values for Azure KeyVault ## [0.3.1] - 2023-03-27 @@ -39,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Support unauthenticated access to the ObservabilityAPI (#126) +- Support unauthenticated access to the ObservabilityAPI (#126) ### Fixed @@ -50,144 +48,144 @@ corresponding [documentation](/docs/migration/Version_0.1.x_0.3.x.md). ### Added -- Add contract id to data source http call (#732) -- Support also support releases in ci pipeline -- Introduce typed object for oauth2 provisioning -- Add documentation -- Add test case -- Add client to omejdn -- add hydra deployment -- Configure dynamically HTTP Receiver callback endpoints. (#685) -- cp-adapter : code review, rollbacke name change (#664) -- Feature/cp adapter task 355 356 357 (#621) -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Add Validity Mapping in ContractDefinitionStepDefs class -- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps -- Add validity attribute in class ContractDefinition -- Local TXDC Setup Documentation (#618) -- Feature: Sftp Provisioner and Client (#554) +- Add contract id to data source http call (#732) +- Support also support releases in ci pipeline +- Introduce typed object for oauth2 provisioning +- Add documentation +- Add test case +- Add client to omejdn +- add hydra deployment +- Configure dynamically HTTP Receiver callback endpoints. (#685) +- cp-adapter : code review, rollbacke name change (#664) +- Feature/cp adapter task 355 356 357 (#621) +- Add Validity Mapping in ContractDefinitionStepDefs class +- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps +- Add validity attribute in class ContractDefinition +- Add Validity Mapping in ContractDefinitionStepDefs class +- Add feature and create SendAnOfferwithoutConstraints method in class negotiationSteps +- Add validity attribute in class ContractDefinition +- Local TXDC Setup Documentation (#618) +- Feature: Sftp Provisioner and Client (#554) ### Changed -- Support horizontal edc scaling in cp adapter extension (#678) -- Use upstream jackson version (#741) -- Replace provision-oauth2 with data-plane-http-oauth2 -- docs: Update sample documentation (#671) -- chore: Disable build ci pipeline if just docu was updated (#705) -- Increase trivy timeout -- Remove not useful anymore custom-jsonld extension (#683) -- update setup docu (#654) -- remove trailing slash (#652) -- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) -- Feature/set charts deprecated (#628) -- update setup docu (#627) -- Feature/update txdc deployment downward capabilities (#625) -- remove git submodule (#619) -- Feature/update postman (#624) -- update control plane docu (#623) -- update postgresql version in Chart.yaml supporting-infrastructure (#622) -- update link to edc logo in README.md (#612) -- update description of supporting infrastructure deployment (#616) +- Support horizontal edc scaling in cp adapter extension (#678) +- Use upstream jackson version (#741) +- Replace provision-oauth2 with data-plane-http-oauth2 +- docs: Update sample documentation (#671) +- chore: Disable build ci pipeline if just docu was updated (#705) +- Increase trivy timeout +- Remove not useful anymore custom-jsonld extension (#683) +- update setup docu (#654) +- remove trailing slash (#652) +- update alpine from 3.17.0 to 3.17.1 for controlplane-memory-hashicorp-vault (#665) +- Feature/set charts deprecated (#628) +- update setup docu (#627) +- Feature/update txdc deployment downward capabilities (#625) +- remove git submodule (#619) +- Feature/update postman (#624) +- update control plane docu (#623) +- update postgresql version in Chart.yaml supporting-infrastructure (#622) +- update link to edc logo in README.md (#612) +- update description of supporting infrastructure deployment (#616) ### Fixed -- bugfix: Fix slow AES encryption (#746) -- Fix typo in tractusx-connector values.yaml comment -- Fix not working docu link in README.md -- Fix typo in control-plane adapter README +- bugfix: Fix slow AES encryption (#746) +- Fix typo in tractusx-connector values.yaml comment +- Fix not working docu link in README.md +- Fix typo in control-plane adapter README ### Dependency updates -- Bump EDC to 20220220 (#767) -- Bump alpine (#749) -- Bump alpine (#750) -- Bump alpine (#752) -- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#753) -- Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) -- Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) -- Bump s3 from 2.19.33 to 2.20.0 -- Bump s3 from 2.19.27 to 2.19.33 -- Bump jaxb-runtime from 4.0.1 to 4.0.2 -- Bump spotless-maven-plugin from 2.31.0 to 2.32.0 -- Bump postgresql from 42.5.1 to 42.5.3 -- Bump nimbus-jose-jwt from 9.30 to 9.30.1 -- Bump lombok from 1.18.24 to 1.18.26 -- Bump flyway-core from 9.12.0 to 9.14.1 -- Bump jackson-bom from 2.14.0-rc2 to 2.14.2 -- Bump cucumber.version from 7.11.0 to 7.11.1 -- Bump azure-sdk-bom from 1.2.8 to 1.2.9 -- Bump mockito-bom from 5.0.0 to 5.1.1 -- Bump edc version to 0.0.1-20230131-SNAPSHOT -- Bump s3 from 2.19.18 to 2.19.27 -- Bump docker/build-push-action from 3 to 4 -- Bump nimbus-jose-jwt from 9.29 to 9.30 -- Bump spotless-maven-plugin from 2.30.0 to 2.31.0 -- Bump nimbus-jose-jwt from 9.28 to 9.29 -- Bump mockito-bom from 4.11.0 to 5.0.0 -- Bump edc version to 0.0.1-20230125-SNAPSHOT -- Bump flyway-core from 9.11.0 to 9.12.0 -- Bump s3 from 2.19.15 to 2.19.18 (#684) -- Bump mikefarah/yq from 4.30.6 to 4.30.8 (#682) -- Bump spotless-maven-plugin from 2.29.0 to 2.30.0 -- Bump edc version to 0.0.1-20230115-SNAPSHOT -- Bump cucumber.version from 7.10.1 to 7.11.0 (#672) -- Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#669) -- Bump s3 from 2.19.11 to 2.19.15 (#668) -- Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) -- Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) -- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#659) -- Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) -- Bump alpine (#658) -- Bump alpine (#661) -- Bump alpine (#662) -- Bump azure/setup-kubectl from 3.1 to 3.2 (#655) -- Bump junit-bom from 5.9.1 to 5.9.2 (#657) -- Bump s3 from 2.19.2 to 2.19.11 (#648) -- Bump actions/checkout from 3.2.0 to 3.3.0 (#647) -- Bump flyway-core from 9.10.2 to 9.11.0 (#646) -- Bump spotless-maven-plugin from 2.28.0 to 2.29.0 (#641) -- Bump mockito-bom from 4.10.0 to 4.11.0 (#637) -- Bump flyway-core from 9.10.1 to 9.10.2 (#632) -- Bump s3 from 2.19.1 to 2.19.2 (#631) -- Bump s3 from 2.18.41 to 2.19.1 (#626) -- Bump mikefarah/yq from 4.30.5 to 4.30.6 (#613) -- Bump cucumber.version from 7.10.0 to 7.10.1 (#614) -- Bump s3 from 2.18.40 to 2.18.41 (#615) -- Bump azure/setup-helm from 3.4 to 3.5 (#596) -- Bump actions/checkout from 3.1.0 to 3.2.0 (#598) -- Bump mockito-bom from 4.9.0 to 4.10.0 (#607) -- Bump s3 from 2.18.39 to 2.18.40 (#609) -- Bump flyway-core from 9.10.0 to 9.10.1 (#610) -- Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) -- Bump s3 from 2.18.35 to 2.18.39 (#606) +- Bump EDC to 20220220 (#767) +- Bump alpine (#749) +- Bump alpine (#750) +- Bump alpine (#752) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#753) +- Bump maven-deploy-plugin from 3.0.0 to 3.1.0 (#735) +- Bump actions/setup-java from 3.9.0 to 3.10.0 (#730) +- Bump s3 from 2.19.33 to 2.20.0 +- Bump s3 from 2.19.27 to 2.19.33 +- Bump jaxb-runtime from 4.0.1 to 4.0.2 +- Bump spotless-maven-plugin from 2.31.0 to 2.32.0 +- Bump postgresql from 42.5.1 to 42.5.3 +- Bump nimbus-jose-jwt from 9.30 to 9.30.1 +- Bump lombok from 1.18.24 to 1.18.26 +- Bump flyway-core from 9.12.0 to 9.14.1 +- Bump jackson-bom from 2.14.0-rc2 to 2.14.2 +- Bump cucumber.version from 7.11.0 to 7.11.1 +- Bump azure-sdk-bom from 1.2.8 to 1.2.9 +- Bump mockito-bom from 5.0.0 to 5.1.1 +- Bump edc version to 0.0.1-20230131-SNAPSHOT +- Bump s3 from 2.19.18 to 2.19.27 +- Bump docker/build-push-action from 3 to 4 +- Bump nimbus-jose-jwt from 9.29 to 9.30 +- Bump spotless-maven-plugin from 2.30.0 to 2.31.0 +- Bump nimbus-jose-jwt from 9.28 to 9.29 +- Bump mockito-bom from 4.11.0 to 5.0.0 +- Bump edc version to 0.0.1-20230125-SNAPSHOT +- Bump flyway-core from 9.11.0 to 9.12.0 +- Bump s3 from 2.19.15 to 2.19.18 (#684) +- Bump mikefarah/yq from 4.30.6 to 4.30.8 (#682) +- Bump spotless-maven-plugin from 2.29.0 to 2.30.0 +- Bump edc version to 0.0.1-20230115-SNAPSHOT +- Bump cucumber.version from 7.10.1 to 7.11.0 (#672) +- Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#669) +- Bump s3 from 2.19.11 to 2.19.15 (#668) +- Bump maven-surefire-plugin from 3.0.0-M7 to 3.0.0-M8 (#670) +- Bump edc version to 0.0.1-20230109-SNAPSHOT (#666) +- Bump alpine in /edc-controlplane/edc-runtime-memory/src/main/docker (#659) +- Bump alpine in /edc-dataplane/edc-dataplane-azure-vault/src/main/docker (#660) +- Bump alpine (#658) +- Bump alpine (#661) +- Bump alpine (#662) +- Bump azure/setup-kubectl from 3.1 to 3.2 (#655) +- Bump junit-bom from 5.9.1 to 5.9.2 (#657) +- Bump s3 from 2.19.2 to 2.19.11 (#648) +- Bump actions/checkout from 3.2.0 to 3.3.0 (#647) +- Bump flyway-core from 9.10.2 to 9.11.0 (#646) +- Bump spotless-maven-plugin from 2.28.0 to 2.29.0 (#641) +- Bump mockito-bom from 4.10.0 to 4.11.0 (#637) +- Bump flyway-core from 9.10.1 to 9.10.2 (#632) +- Bump s3 from 2.19.1 to 2.19.2 (#631) +- Bump s3 from 2.18.41 to 2.19.1 (#626) +- Bump mikefarah/yq from 4.30.5 to 4.30.6 (#613) +- Bump cucumber.version from 7.10.0 to 7.10.1 (#614) +- Bump s3 from 2.18.40 to 2.18.41 (#615) +- Bump azure/setup-helm from 3.4 to 3.5 (#596) +- Bump actions/checkout from 3.1.0 to 3.2.0 (#598) +- Bump mockito-bom from 4.9.0 to 4.10.0 (#607) +- Bump s3 from 2.18.39 to 2.18.40 (#609) +- Bump flyway-core from 9.10.0 to 9.10.1 (#610) +- Bump actions/setup-java from 3.8.0 to 3.9.0 (#605) +- Bump s3 from 2.18.35 to 2.18.39 (#606) ## [0.1.6] - 2023-02-20 ### Fixed -- SQL leakage issue -- Catalog pagination +- SQL leakage issue +- Catalog pagination ## [0.1.5] - 2023-02-13 ### Fixed -- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug -- Data Encryption extension: fixed usage of a blocking algorithm +- Use patched EDC version: 0.0.1-20220922.2-SNAPSHOT to fix catalog pagination bug +- Data Encryption extension: fixed usage of a blocking algorithm ## [0.1.2] - 2022-09-30 ### Added -- Introduced DEPENDENCIES file +- Introduced DEPENDENCIES file ### Changed -- Moved helm charts from `deployment/helm` to `charts` -- Replaced distroless image with alpine in all docker images -- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` +- Moved helm charts from `deployment/helm` to `charts` +- Replaced distroless image with alpine in all docker images +- Update EDC commit to `740c100ac162bc41b1968c232ad81f7d739aefa9` ## [0.1.1] - 2022-09-04 @@ -196,17 +194,17 @@ connector. [documentation](/docs/migration/Version_0.1.0_0.1.1.md). ### Added -- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) +- Control-Plane Extension ([cx-oauth2](/edc-extensions/cx-oauth2/README.md)) ### Changed -- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) -- Helm Charts: TLS secret name is now configurable +- Introduced git submodule to import EDC dependencies (instead of snapshot- or milestone artifact) +- Helm Charts: TLS secret name is now configurable ### Fixed -- Connectors with Azure Vault extension are now starting - again [link](https://github.com/eclipse-edc/Connector/issues/1892) +- Connectors with Azure Vault extension are now starting + again [link](https://github.com/eclipse-edc/Connector/issues/1892) ## [0.1.0] - 2022-08-19 @@ -215,74 +213,74 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ### Added -- Control-Plane - extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) - - run the EDC with multiple data planes at once -- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) - - add data plane instances to the control plane by configuration -- Data-Plane - extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) - - transfer from and to AWS S3 buckets -- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) - - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) +- Control-Plane + extension ([data-plane-selector-client](https://github.com/eclipse-edc/Connector/tree/v0.0.1-milestone-5/extensions/data-plane-selector/selector-client)) + - run the EDC with multiple data planes at once +- Control-Plane extension ([dataplane-selector-configuration](edc-extensions/dataplane-selector-configuration)) + - add data plane instances to the control plane by configuration +- Data-Plane + extension ([s3-data-plane](https://github.com/eclipse-edc/Connector/tree/main/extensions/aws/data-plane-s3)) + - transfer from and to AWS S3 buckets +- Control-Plane extension ([data-encryption](edc-extensions/data-encryption)) + - Data-Plane authentication attribute transmitted during data-plane-transfer can be encrypted symmetrically (AES) ### Changed -- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) -- EDC has been updated to - version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - - implications to the behavior of the connector have been covered in - the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) +- Update setting name (`edc.dataplane.token.validation.endpoint` -> `edc.dataplane.token.validation.endpoint`) +- EDC has been updated to + version [0.0.1-20220818-SNAPSHOT](https://oss.sonatype.org/#nexus-search;gav~org.eclipse.dataspaceconnector~~0.0.1-20220818-SNAPSHOT~~) - + implications to the behavior of the connector have been covered in + the [corresponding migration guide](docs/migration/Version_0.0.x_0.1.x.md) ### Fixed -- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving - offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) -- Deletion of Asset becomes impossible when Contract Negotiation - exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) -- Deletion of Policy becomes impossible when Contract Definition - exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) +- Contract-Offer-Receiving-Connectors must also pass the ContractPolicy of the ContractDefinition before receiving + offers([issue](https://github.com/eclipse-edc/Connector/issues/1331)) +- Deletion of Asset becomes impossible when Contract Negotiation + exists([issue](https://github.com/eclipse-edc/Connector/issues/1403)) +- Deletion of Policy becomes impossible when Contract Definition + exists([issue](https://github.com/eclipse-edc/Connector/issues/1410)) ## [0.0.6] - 2022-07-29 ### Fixed -- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath - issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) - library +- Fixes [release 0.0.5](https://github.com/eclipse-tractusx/tractusx-edc/releases/tag/0.0.5), which introduced classpath + issues due to usage of [net.jodah:failsafe:2.4.3](https://search.maven.org/artifact/net.jodah/failsafe/2.4.3/jar) + library ## [0.0.5] - 2022-07-28 ### Added -- EDC Health Checks for HashiCorp Vault +- EDC Health Checks for HashiCorp Vault ### Changed -- BusinessPartnerNumber constraint supports List structure -- Helm: Confidential EDC settings can be set using k8s secrets -- HashiCorp Vault API path configurable +- BusinessPartnerNumber constraint supports List structure +- Helm: Confidential EDC settings can be set using k8s secrets +- HashiCorp Vault API path configurable ## [0.0.4] - 2022-06-27 ### Added -- HashiCorp Vault Extension -- Control Plane with HashiCorp Vault and PostgreSQL support +- HashiCorp Vault Extension +- Control Plane with HashiCorp Vault and PostgreSQL support ### Changed -- Release Workflow now publishes EDC Extensions as Maven Artifacts +- Release Workflow now publishes EDC Extensions as Maven Artifacts ### Fixed -- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 - contract offers max. +- [#1515](https://github.com/eclipse-edc/Connector/issues/1515) SQL: Connector sends out 50 + contract offers max. ### Removed -- CosmosDB Control Plane -- Control API Extension for all Control Planes +- CosmosDB Control Plane +- Control API Extension for all Control Planes ## [0.0.3] - 2022-05-23 @@ -290,9 +288,7 @@ corresponding [documentation](/docs/migration/Version_0.0.x_0.1.x.md). ## [0.0.1] - 2022-05-13 -[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.4...HEAD - -[0.3.4]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.3...0.3.4 +[Unreleased]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.3...HEAD [0.3.3]: https://github.com/eclipse-tractusx/tractusx-edc/compare/0.3.2...0.3.3