From 97c52d45d7c86eea32596cedd4301dba0b4f4cd9 Mon Sep 17 00:00:00 2001 From: Boris Lavrishchev Date: Thu, 20 Feb 2025 16:55:32 +0300 Subject: [PATCH 1/6] feat: Update maven-release.yaml Changed maven release worflow sequence and added optional docker release --- .github/workflows/maven-release.yaml | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/maven-release.yaml b/.github/workflows/maven-release.yaml index 182d2c0..b6f9bb9 100644 --- a/.github/workflows/maven-release.yaml +++ b/.github/workflows/maven-release.yaml @@ -6,6 +6,10 @@ # - Update pom.xml: Updates the version in the pom.xml file using the extracted release version. # - Publish to Maven: Publishes the artifact to the Maven repository using the updated pom.xml. +# To make it work for your project, you need to adjust the pom.xml and add configuration file for GitHub release. +# Please find detailed instructions: +# https://github.com/Netcracker/qubership-workflow-hub?tab=readme-ov-file#maven-project-release-workflow + name: Release And Upload to Maven Central on: @@ -13,9 +17,9 @@ on: inputs: version: required: true - default: '2025.1-1.0.0' + default: '' type: string - description: 'Release version (e.g., 2025.1-1.0.0)' + description: 'Release version (e.g., 1.0.0)' java_version: required: false type: string @@ -60,7 +64,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + with: + fetch-depth: 0 - name: Update pom.xml uses: Netcracker/qubership-workflow-hub/actions/pom-updater@main with: @@ -74,14 +79,13 @@ jobs: needs: [update-pom-version] uses: Netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main with: - maven_command: "--batch-mode deploy" - java_version: ${{ github.event.inputs.java_version }} - version: ${{ github.event.inputs.version }} + maven-command: "--batch-mode deploy" + java-version: ${{ github.event.inputs.java_version }} secrets: - maven_username: ${{ secrets.MAVEN_USER }} - maven_password: ${{ secrets.MAVEN_PASSWORD }} - maven_gpg_passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - maven_gpg_private_key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + maven-username: ${{ secrets.MAVEN_USER }} + maven-token: ${{ secrets.MAVEN_PASSWORD }} + gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} github-release: needs: [upload_to_maven_central] From 6cc0cdc10070d21d6f97eae7b625051fb5432bee Mon Sep 17 00:00:00 2001 From: Boris Lavrishchev Date: Thu, 20 Feb 2025 17:01:38 +0300 Subject: [PATCH 2/6] feat: Update maven-release.yaml Changed maven release worflow sequence and added optional docker release --- .github/workflows/maven-release.yaml | 95 +++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/.github/workflows/maven-release.yaml b/.github/workflows/maven-release.yaml index b6f9bb9..e8b388a 100644 --- a/.github/workflows/maven-release.yaml +++ b/.github/workflows/maven-release.yaml @@ -2,29 +2,45 @@ # This GitHub Actions workflow is designed to be triggered when a release is marked as a full release. # The workflow performs the following tasks: -# - Extract Release Version: Extracts the tag name and removes the leading 'v' character to get the release version. -# - Update pom.xml: Updates the version in the pom.xml file using the extracted release version. -# - Publish to Maven: Publishes the artifact to the Maven repository using the updated pom.xml. +# 1. Checks if the tag already exists. +# 2. Updates the version in the pom.xml file. +# 3. Commits the changes to the repository. +# 4. Builds the project using Maven. +# 5. Runs tests. +# 6. Tags the commit with the release version. +# 7. Deploys the artifact to the Maven repository. +# 8. Builds and publishes a Docker image. +# 9. Creates a GitHub release. # To make it work for your project, you need to adjust the pom.xml and add configuration file for GitHub release. # Please find detailed instructions: # https://github.com/Netcracker/qubership-workflow-hub?tab=readme-ov-file#maven-project-release-workflow -name: Release And Upload to Maven Central +name: Release And Deploy Maven Artifact on: workflow_dispatch: inputs: version: required: true - default: '' + default: '1.0.0' type: string description: 'Release version (e.g., 1.0.0)' - java_version: + java-version: required: false type: string default: "21" description: 'Java version (e.g., 21)' + build-docker: + required: false + type: boolean + default: true + description: 'Release docker image if there is Docker file' + dry-run: + required: false + type: boolean + default: false + description: 'Dry run' jobs: check-tag: @@ -33,7 +49,7 @@ jobs: - name: Input parameters run: | echo "Version: ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "Java version: ${{ github.event.inputs.java_version }}" >> $GITHUB_STEP_SUMMARY + echo "Java version: ${{ github.event.inputs.java-version }}" >> $GITHUB_STEP_SUMMARY - name: Checkout code uses: actions/checkout@v4 @@ -61,35 +77,84 @@ jobs: update-pom-version: needs: [check-tag] runs-on: ubuntu-latest + outputs: + artifact_id: ${{ steps.config.outputs.artifact_id }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Update pom.xml - uses: Netcracker/qubership-workflow-hub/actions/pom-updater@main + id: config + uses: netcracker/qubership-workflow-hub/actions/pom-updater@main with: new_value: ${{ github.event.inputs.version }} + - name: Commit Changes - uses: Netcracker/qubership-workflow-hub/actions/commit-and-push@main + uses: netcracker/qubership-workflow-hub/actions/commit-and-push@main with: commit_message: "Update pom.xml version to ${{ github.event.inputs.version }}" - upload_to_maven_central: + mvn-package: needs: [update-pom-version] - uses: Netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main + uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main + with: + maven-command: "--batch-mode package" + java-version: ${{ github.event.inputs.java-version }} + version: ${{ github.event.inputs.version }} + upload-artifact: true + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + secrets: + maven-username: ${{ secrets.MAVEN_USER }} + maven-token: ${{ secrets.MAVEN_PASSWORD }} + gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + + tests: + needs: [mvn-package] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run tests + run: echo "Running tests here" + + tag: + needs: [tests] + uses: netcracker/qubership-workflow-hub/.github/workflows/tag-creator.yml@main + with: + tag-name: ${{ github.event.inputs.version }} + + mvn-deploy: + if: ${{ inputs.dry-run != 'true' }} + needs: [update-pom-version, tag] + uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main with: - maven-command: "--batch-mode deploy" - java-version: ${{ github.event.inputs.java_version }} + maven-command: ${{ (github.event.inputs.dry-run == 'true' && '--batch-mode package') || '--batch-mode deploy' }} + java-version: ${{ github.event.inputs.java-version }} + upload-artifact: false + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + server-id: "central" secrets: maven-username: ${{ secrets.MAVEN_USER }} maven-token: ${{ secrets.MAVEN_PASSWORD }} gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + docker-build-publish: + if: ${{ github.event.inputs.build-docker == 'true' }} && ${{ hashFiles('Dockerfile') != '' }} + needs: [update-pom-version, tag] + uses: netcracker/qubership-workflow-hub/.github/workflows/docker-publish.yml@main + with: + ref: ${{ github.event.inputs.version }} + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + dry-run: ${{ inputs.dry-run }} + github-release: - needs: [upload_to_maven_central] - uses: Netcracker/qubership-workflow-hub/.github/workflows/release-drafter.yml@main + needs: [tag] + uses: netcracker/qubership-workflow-hub/.github/workflows/release-drafter.yml@main with: version: ${{ github.event.inputs.version }} publish: false From b916ae1bc766c2b71ced153de12457bdafe680f7 Mon Sep 17 00:00:00 2001 From: borislavr Date: Thu, 20 Feb 2025 17:22:57 +0300 Subject: [PATCH 3/6] feat: Add workflow for deploying Maven snapshot artifacts to GitHub packages --- .github/workflows/maven-snapshot-deploy.yaml | 39 ++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/maven-snapshot-deploy.yaml diff --git a/.github/workflows/maven-snapshot-deploy.yaml b/.github/workflows/maven-snapshot-deploy.yaml new file mode 100644 index 0000000..18acaf2 --- /dev/null +++ b/.github/workflows/maven-snapshot-deploy.yaml @@ -0,0 +1,39 @@ +--- +# The workflow to deploy snapshot artifact versions to GitHub packages +# Fill free to adjust java version and additional mvn command-line parameters +# The workflow will trigger on pushes into branches different from main and release +# Please make sure that the version in the pom.xml file has the SNAPSHOT postfix + +name: Maven deploy snapshot + +on: + push: + branches: + - "!main" + - "!**release*" + +permissions: + packages: write + +jobs: + mvn-deploy: + runs-on: ubuntu-latest + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: "21" + distribution: "temurin" + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + - name: Display settings.xml + run: cat ~/.m2/settings.xml + - name: "Maven deploy" + run: | + mvn --batch-mode deploy -DaltDeploymentRepository=github::https://maven.pkg.github.com/${{ github.repository }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_USER: ${{ github.actor }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} From 60f16e4badfeff964fe0f598d918da6952d1f81d Mon Sep 17 00:00:00 2001 From: borislavr Date: Fri, 21 Feb 2025 11:07:37 +0300 Subject: [PATCH 4/6] feat: Enhance maven-release workflow with Dockerfile existence check and improved job dependencies --- .github/workflows/maven-release.yaml | 334 ++++++++++++++------------- 1 file changed, 176 insertions(+), 158 deletions(-) diff --git a/.github/workflows/maven-release.yaml b/.github/workflows/maven-release.yaml index e8b388a..7d9e006 100644 --- a/.github/workflows/maven-release.yaml +++ b/.github/workflows/maven-release.yaml @@ -1,160 +1,178 @@ --- -# This GitHub Actions workflow is designed to be triggered when a release is marked as a full release. -# The workflow performs the following tasks: -# 1. Checks if the tag already exists. -# 2. Updates the version in the pom.xml file. -# 3. Commits the changes to the repository. -# 4. Builds the project using Maven. -# 5. Runs tests. -# 6. Tags the commit with the release version. -# 7. Deploys the artifact to the Maven repository. -# 8. Builds and publishes a Docker image. -# 9. Creates a GitHub release. - -# To make it work for your project, you need to adjust the pom.xml and add configuration file for GitHub release. -# Please find detailed instructions: -# https://github.com/Netcracker/qubership-workflow-hub?tab=readme-ov-file#maven-project-release-workflow - -name: Release And Deploy Maven Artifact - -on: - workflow_dispatch: - inputs: - version: - required: true - default: '1.0.0' - type: string - description: 'Release version (e.g., 1.0.0)' - java-version: - required: false - type: string - default: "21" - description: 'Java version (e.g., 21)' - build-docker: - required: false - type: boolean - default: true - description: 'Release docker image if there is Docker file' - dry-run: - required: false - type: boolean - default: false - description: 'Dry run' - -jobs: - check-tag: - runs-on: ubuntu-latest - steps: - - name: Input parameters - run: | - echo "Version: ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "Java version: ${{ github.event.inputs.java-version }}" >> $GITHUB_STEP_SUMMARY - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Check if tag exists - id: check_tag - uses: netcracker/qubership-workflow-hub/actions/tag-checker@main - with: - tag: 'v${{ github.event.inputs.version }}' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Output result - run: | - echo "Tag exists: ${{ steps.check_tag.outputs.exists }}" - echo "Tag name: v${{ github.event.inputs.version }}" - - - name: Fail if tag exists - if: steps.check_tag.outputs.exists == 'true' - run: | - echo "Tag already exists: v${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "Tag already exists: v${{ github.event.inputs.version }}" - exit 1 - - update-pom-version: - needs: [check-tag] - runs-on: ubuntu-latest - outputs: - artifact_id: ${{ steps.config.outputs.artifact_id }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Update pom.xml - id: config - uses: netcracker/qubership-workflow-hub/actions/pom-updater@main - with: - new_value: ${{ github.event.inputs.version }} - - - name: Commit Changes - uses: netcracker/qubership-workflow-hub/actions/commit-and-push@main - with: - commit_message: "Update pom.xml version to ${{ github.event.inputs.version }}" - - mvn-package: - needs: [update-pom-version] - uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main - with: - maven-command: "--batch-mode package" - java-version: ${{ github.event.inputs.java-version }} - version: ${{ github.event.inputs.version }} - upload-artifact: true - artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} - secrets: - maven-username: ${{ secrets.MAVEN_USER }} - maven-token: ${{ secrets.MAVEN_PASSWORD }} - gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} - - tests: - needs: [mvn-package] - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Run tests - run: echo "Running tests here" - - tag: - needs: [tests] - uses: netcracker/qubership-workflow-hub/.github/workflows/tag-creator.yml@main - with: - tag-name: ${{ github.event.inputs.version }} - - mvn-deploy: - if: ${{ inputs.dry-run != 'true' }} - needs: [update-pom-version, tag] - uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main - with: - maven-command: ${{ (github.event.inputs.dry-run == 'true' && '--batch-mode package') || '--batch-mode deploy' }} - java-version: ${{ github.event.inputs.java-version }} - upload-artifact: false - artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} - server-id: "central" - secrets: - maven-username: ${{ secrets.MAVEN_USER }} - maven-token: ${{ secrets.MAVEN_PASSWORD }} - gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} - - docker-build-publish: - if: ${{ github.event.inputs.build-docker == 'true' }} && ${{ hashFiles('Dockerfile') != '' }} - needs: [update-pom-version, tag] - uses: netcracker/qubership-workflow-hub/.github/workflows/docker-publish.yml@main - with: - ref: ${{ github.event.inputs.version }} - artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} - dry-run: ${{ inputs.dry-run }} - - github-release: - needs: [tag] - uses: netcracker/qubership-workflow-hub/.github/workflows/release-drafter.yml@main - with: - version: ${{ github.event.inputs.version }} - publish: false + # This GitHub Actions workflow is designed to be triggered when a release is marked as a full release. + # The workflow performs the following tasks: + # 1. Checks if the tag already exists. + # 2. Updates the version in the pom.xml file. + # 3. Commits the changes to the repository. + # 4. Builds the project using Maven. + # 5. Runs tests. + # 6. Tags the commit with the release version. + # 7. Deploys the artifact to the Maven repository. + # 8. Builds and publishes a Docker image. + # 9. Creates a GitHub release. + + # To make it work for your project, you need to adjust the pom.xml and add configuration file for GitHub release. + # Please find detailed instructions: + # https://github.com/Netcracker/qubership-workflow-hub?tab=readme-ov-file#maven-project-release-workflow + + name: Release And Deploy Maven Artifact + + on: + workflow_dispatch: + inputs: + version: + required: true + default: '1.0.0' + type: string + description: 'Release version (e.g., 1.0.0)' + java-version: + required: false + type: string + default: "21" + description: 'Java version (e.g., 21)' + build-docker: + required: false + type: boolean + default: true + description: 'Release docker image if there is Docker file' + dry-run: + required: false + type: boolean + default: false + description: 'Dry run' + + jobs: + check-tag: + runs-on: ubuntu-latest + steps: + - name: Input parameters + run: | + echo "Version: ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "Java version: ${{ github.event.inputs.java-version }}" >> $GITHUB_STEP_SUMMARY + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check if tag exists + id: check_tag + uses: netcracker/qubership-workflow-hub/actions/tag-checker@main + with: + tag: 'v${{ github.event.inputs.version }}' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Output result + run: | + echo "Tag exists: ${{ steps.check_tag.outputs.exists }}" + echo "Tag name: v${{ github.event.inputs.version }}" + + - name: Fail if tag exists + if: steps.check_tag.outputs.exists == 'true' + run: | + echo "Tag already exists: v${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "Tag already exists: v${{ github.event.inputs.version }}" + exit 1 + + update-pom-version: + needs: [check-tag] + runs-on: ubuntu-latest + outputs: + artifact_id: ${{ steps.config.outputs.artifact_id }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Update pom.xml + id: config + uses: netcracker/qubership-workflow-hub/actions/pom-updater@main + with: + new_value: ${{ github.event.inputs.version }} + + - name: Commit Changes + uses: netcracker/qubership-workflow-hub/actions/commit-and-push@main + with: + commit_message: "Update pom.xml version to ${{ github.event.inputs.version }}" + + mvn-package: + needs: [update-pom-version] + uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main + with: + maven-command: "--batch-mode package" + java-version: ${{ github.event.inputs.java-version }} + upload-artifact: true + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + secrets: + maven-username: ${{ secrets.MAVEN_USER }} + maven-token: ${{ secrets.MAVEN_PASSWORD }} + gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + + tests: + needs: [mvn-package] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run tests + run: echo "Running tests here" + + tag: + needs: [tests] + uses: netcracker/qubership-workflow-hub/.github/workflows/tag-creator.yml@main + with: + tag-name: ${{ github.event.inputs.version }} + + mvn-deploy: + needs: [update-pom-version, tag] + uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main + with: + maven-command: ${{ (github.event.inputs.dry-run == 'true' && '--batch-mode package') || '--batch-mode deploy' }} + java-version: ${{ github.event.inputs.java-version }} + upload-artifact: false + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + server-id: "central" + secrets: + maven-username: ${{ secrets.MAVEN_USER }} + maven-token: ${{ secrets.MAVEN_PASSWORD }} + gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + + check-dockerfile: + runs-on: ubuntu-latest + needs: [update-pom-version, tag] + outputs: + dockerfile_exists: ${{ steps.check_dockerfile.outputs.df_exists }} + steps: + - uses: actions/checkout@v4 + - name: "Check Dockerfile existence" + id: check_dockerfile + shell: bash + run: | + if [[ -f Dockerfile ]]; then + echo "df_exists=exists" >> "$GITHUB_OUTPUT" + else + echo "Dockerfile does not exist. Docker build stage will be skipped" + echo "df_exists=notexists" >> "$GITHUB_OUTPUT" + fi + echo "GITHUB_OUTPUT:" + cat "$GITHUB_OUTPUT" + + docker-build-publish: + needs: [update-pom-version, tag, check-dockerfile] + if: ${{ github.event.inputs.build-docker == 'true' && needs.check-dockerfile.outputs.dockerfile_exists == 'exists' }} + uses: netcracker/qubership-workflow-hub/.github/workflows/docker-publish.yml@main + with: + ref: ${{ github.event.inputs.version }} + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + dry-run: ${{ inputs.dry-run }} + + github-release: + needs: [tag] + uses: netcracker/qubership-workflow-hub/.github/workflows/release-drafter.yml@main + with: + version: ${{ github.event.inputs.version }} + publish: false \ No newline at end of file From b31695314ed553fe3d2b8344d9a58dc866b6b808 Mon Sep 17 00:00:00 2001 From: borislavr Date: Fri, 21 Feb 2025 11:21:46 +0300 Subject: [PATCH 5/6] feat: Update maven-release workflow with improved comments and structure --- .github/workflows/maven-release.yaml | 352 +++++++++++++-------------- 1 file changed, 176 insertions(+), 176 deletions(-) diff --git a/.github/workflows/maven-release.yaml b/.github/workflows/maven-release.yaml index 7d9e006..d03673f 100644 --- a/.github/workflows/maven-release.yaml +++ b/.github/workflows/maven-release.yaml @@ -1,178 +1,178 @@ --- - # This GitHub Actions workflow is designed to be triggered when a release is marked as a full release. - # The workflow performs the following tasks: - # 1. Checks if the tag already exists. - # 2. Updates the version in the pom.xml file. - # 3. Commits the changes to the repository. - # 4. Builds the project using Maven. - # 5. Runs tests. - # 6. Tags the commit with the release version. - # 7. Deploys the artifact to the Maven repository. - # 8. Builds and publishes a Docker image. - # 9. Creates a GitHub release. - - # To make it work for your project, you need to adjust the pom.xml and add configuration file for GitHub release. - # Please find detailed instructions: - # https://github.com/Netcracker/qubership-workflow-hub?tab=readme-ov-file#maven-project-release-workflow - - name: Release And Deploy Maven Artifact - - on: - workflow_dispatch: - inputs: - version: - required: true - default: '1.0.0' - type: string - description: 'Release version (e.g., 1.0.0)' - java-version: - required: false - type: string - default: "21" - description: 'Java version (e.g., 21)' - build-docker: - required: false - type: boolean - default: true - description: 'Release docker image if there is Docker file' - dry-run: - required: false - type: boolean - default: false - description: 'Dry run' - - jobs: - check-tag: - runs-on: ubuntu-latest - steps: - - name: Input parameters - run: | - echo "Version: ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "Java version: ${{ github.event.inputs.java-version }}" >> $GITHUB_STEP_SUMMARY - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Check if tag exists - id: check_tag - uses: netcracker/qubership-workflow-hub/actions/tag-checker@main - with: - tag: 'v${{ github.event.inputs.version }}' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Output result - run: | - echo "Tag exists: ${{ steps.check_tag.outputs.exists }}" - echo "Tag name: v${{ github.event.inputs.version }}" - - - name: Fail if tag exists - if: steps.check_tag.outputs.exists == 'true' - run: | - echo "Tag already exists: v${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "Tag already exists: v${{ github.event.inputs.version }}" - exit 1 - - update-pom-version: - needs: [check-tag] - runs-on: ubuntu-latest - outputs: - artifact_id: ${{ steps.config.outputs.artifact_id }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Update pom.xml - id: config - uses: netcracker/qubership-workflow-hub/actions/pom-updater@main - with: - new_value: ${{ github.event.inputs.version }} - - - name: Commit Changes - uses: netcracker/qubership-workflow-hub/actions/commit-and-push@main - with: - commit_message: "Update pom.xml version to ${{ github.event.inputs.version }}" - - mvn-package: - needs: [update-pom-version] - uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main - with: - maven-command: "--batch-mode package" - java-version: ${{ github.event.inputs.java-version }} - upload-artifact: true - artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} - secrets: - maven-username: ${{ secrets.MAVEN_USER }} - maven-token: ${{ secrets.MAVEN_PASSWORD }} - gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} - - tests: - needs: [mvn-package] - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Run tests - run: echo "Running tests here" - - tag: - needs: [tests] - uses: netcracker/qubership-workflow-hub/.github/workflows/tag-creator.yml@main - with: - tag-name: ${{ github.event.inputs.version }} - - mvn-deploy: - needs: [update-pom-version, tag] - uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main - with: - maven-command: ${{ (github.event.inputs.dry-run == 'true' && '--batch-mode package') || '--batch-mode deploy' }} - java-version: ${{ github.event.inputs.java-version }} - upload-artifact: false - artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} - server-id: "central" - secrets: - maven-username: ${{ secrets.MAVEN_USER }} - maven-token: ${{ secrets.MAVEN_PASSWORD }} - gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} - gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} - - check-dockerfile: - runs-on: ubuntu-latest - needs: [update-pom-version, tag] - outputs: - dockerfile_exists: ${{ steps.check_dockerfile.outputs.df_exists }} - steps: - - uses: actions/checkout@v4 - - name: "Check Dockerfile existence" - id: check_dockerfile - shell: bash - run: | - if [[ -f Dockerfile ]]; then - echo "df_exists=exists" >> "$GITHUB_OUTPUT" - else - echo "Dockerfile does not exist. Docker build stage will be skipped" - echo "df_exists=notexists" >> "$GITHUB_OUTPUT" - fi - echo "GITHUB_OUTPUT:" - cat "$GITHUB_OUTPUT" - - docker-build-publish: - needs: [update-pom-version, tag, check-dockerfile] - if: ${{ github.event.inputs.build-docker == 'true' && needs.check-dockerfile.outputs.dockerfile_exists == 'exists' }} - uses: netcracker/qubership-workflow-hub/.github/workflows/docker-publish.yml@main - with: - ref: ${{ github.event.inputs.version }} - artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} - dry-run: ${{ inputs.dry-run }} - - github-release: - needs: [tag] - uses: netcracker/qubership-workflow-hub/.github/workflows/release-drafter.yml@main - with: - version: ${{ github.event.inputs.version }} - publish: false \ No newline at end of file +# This GitHub Actions workflow is designed to be triggered when a release is marked as a full release. +# The workflow performs the following tasks: +# 1. Checks if the tag already exists. +# 2. Updates the version in the pom.xml file. +# 3. Commits the changes to the repository. +# 4. Builds the project using Maven. +# 5. Runs tests. +# 6. Tags the commit with the release version. +# 7. Deploys the artifact to the Maven repository. +# 8. Builds and publishes a Docker image. +# 9. Creates a GitHub release. + +# To make it work for your project, you need to adjust the pom.xml and add configuration file for GitHub release. +# Please find detailed instructions: +# https://github.com/Netcracker/qubership-workflow-hub?tab=readme-ov-file#maven-project-release-workflow + +name: Release And Deploy Maven Artifact + +on: + workflow_dispatch: + inputs: + version: + required: true + default: '1.0.0' + type: string + description: 'Release version (e.g., 1.0.0)' + java-version: + required: false + type: string + default: "21" + description: 'Java version (e.g., 21)' + build-docker: + required: false + type: boolean + default: true + description: 'Release docker image if there is Docker file' + dry-run: + required: false + type: boolean + default: false + description: 'Dry run' + +jobs: + check-tag: + runs-on: ubuntu-latest + steps: + - name: Input parameters + run: | + echo "Version: ${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "Java version: ${{ github.event.inputs.java-version }}" >> $GITHUB_STEP_SUMMARY + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check if tag exists + id: check_tag + uses: netcracker/qubership-workflow-hub/actions/tag-checker@main + with: + tag: 'v${{ github.event.inputs.version }}' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Output result + run: | + echo "Tag exists: ${{ steps.check_tag.outputs.exists }}" + echo "Tag name: v${{ github.event.inputs.version }}" + + - name: Fail if tag exists + if: steps.check_tag.outputs.exists == 'true' + run: | + echo "Tag already exists: v${{ github.event.inputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "Tag already exists: v${{ github.event.inputs.version }}" + exit 1 + + update-pom-version: + needs: [check-tag] + runs-on: ubuntu-latest + outputs: + artifact_id: ${{ steps.config.outputs.artifact_id }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Update pom.xml + id: config + uses: netcracker/qubership-workflow-hub/actions/pom-updater@main + with: + new_value: ${{ github.event.inputs.version }} + + - name: Commit Changes + uses: netcracker/qubership-workflow-hub/actions/commit-and-push@main + with: + commit_message: "Update pom.xml version to ${{ github.event.inputs.version }}" + + mvn-package: + needs: [update-pom-version] + uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main + with: + maven-command: "--batch-mode package" + java-version: ${{ github.event.inputs.java-version }} + upload-artifact: true + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + secrets: + maven-username: ${{ secrets.MAVEN_USER }} + maven-token: ${{ secrets.MAVEN_PASSWORD }} + gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + + tests: + needs: [mvn-package] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run tests + run: echo "Running tests here" + + tag: + needs: [tests] + uses: netcracker/qubership-workflow-hub/.github/workflows/tag-creator.yml@main + with: + tag-name: ${{ github.event.inputs.version }} + + mvn-deploy: + needs: [update-pom-version, tag] + uses: netcracker/qubership-workflow-hub/.github/workflows/maven-publish.yml@main + with: + maven-command: ${{ (github.event.inputs.dry-run == 'true' && '--batch-mode package') || '--batch-mode deploy' }} + java-version: ${{ github.event.inputs.java-version }} + upload-artifact: false + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + server-id: "central" + secrets: + maven-username: ${{ secrets.MAVEN_USER }} + maven-token: ${{ secrets.MAVEN_PASSWORD }} + gpg-passphrase: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + + check-dockerfile: + runs-on: ubuntu-latest + needs: [update-pom-version, tag] + outputs: + dockerfile_exists: ${{ steps.check_dockerfile.outputs.df_exists }} + steps: + - uses: actions/checkout@v4 + - name: "Check Dockerfile existence" + id: check_dockerfile + shell: bash + run: | + if [[ -f Dockerfile ]]; then + echo "df_exists=exists" >> "$GITHUB_OUTPUT" + else + echo "Dockerfile does not exist. Docker build stage will be skipped" + echo "df_exists=notexists" >> "$GITHUB_OUTPUT" + fi + echo "GITHUB_OUTPUT:" + cat "$GITHUB_OUTPUT" + + docker-build-publish: + needs: [update-pom-version, tag, check-dockerfile] + if: ${{ github.event.inputs.build-docker == 'true' && needs.check-dockerfile.outputs.dockerfile_exists == 'exists' }} + uses: netcracker/qubership-workflow-hub/.github/workflows/docker-publish.yml@main + with: + ref: ${{ github.event.inputs.version }} + artifact-id: ${{ needs.update-pom-version.outputs.artifact_id }} + dry-run: ${{ inputs.dry-run }} + + github-release: + needs: [tag] + uses: netcracker/qubership-workflow-hub/.github/workflows/release-drafter.yml@main + with: + version: ${{ github.event.inputs.version }} + publish: false \ No newline at end of file From cd65201b324fc0dc3bce10c0711d3147a60b7975 Mon Sep 17 00:00:00 2001 From: Andrew Chumak <442045+NuTTeR@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:45:13 +0300 Subject: [PATCH 6/6] Added new feature - live exchanges (#25) --- .../ChainGlobalExceptionHandler.java | 12 ++ .../HttpTriggerFinishProcessor.java | 8 +- .../camel/processors/SplitAsyncProcessor.java | 32 +++++ .../ChainExecutionTerminatedException.java | 23 ++++ .../ChainExecutionTimeoutException.java | 23 ++++ .../errorhandling/errorcode/ErrorCode.java | 2 + .../model/constants/CamelConstants.java | 5 + .../controller/LiveExchangesController.java | 70 +++++++++++ .../engine/rest/v1/dto/LiveExchangeDTO.java | 30 +++++ .../service/IntegrationRuntimeService.java | 3 +- .../engine/service/LiveExchangesService.java | 113 ++++++++++++++++++ .../service/debugger/CamelDebugger.java | 23 +++- .../service/debugger/util/DebuggerUtils.java | 5 + 13 files changed, 342 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/qubership/integration/platform/engine/camel/processors/SplitAsyncProcessor.java create mode 100644 src/main/java/org/qubership/integration/platform/engine/errorhandling/ChainExecutionTerminatedException.java create mode 100644 src/main/java/org/qubership/integration/platform/engine/errorhandling/ChainExecutionTimeoutException.java create mode 100644 src/main/java/org/qubership/integration/platform/engine/rest/v1/controller/LiveExchangesController.java create mode 100644 src/main/java/org/qubership/integration/platform/engine/rest/v1/dto/LiveExchangeDTO.java create mode 100644 src/main/java/org/qubership/integration/platform/engine/service/LiveExchangesService.java diff --git a/src/main/java/org/qubership/integration/platform/engine/camel/components/servlet/exception/ChainGlobalExceptionHandler.java b/src/main/java/org/qubership/integration/platform/engine/camel/components/servlet/exception/ChainGlobalExceptionHandler.java index f30d62f..32dd8b3 100644 --- a/src/main/java/org/qubership/integration/platform/engine/camel/components/servlet/exception/ChainGlobalExceptionHandler.java +++ b/src/main/java/org/qubership/integration/platform/engine/camel/components/servlet/exception/ChainGlobalExceptionHandler.java @@ -20,6 +20,8 @@ import org.qubership.integration.platform.engine.camel.components.directvm.ChainConsumerNotAvailableException; import org.qubership.integration.platform.engine.camel.components.servlet.exception.annotations.ChainExceptionHandler; import org.qubership.integration.platform.engine.camel.exceptions.IterationLimitException; +import org.qubership.integration.platform.engine.errorhandling.ChainExecutionTerminatedException; +import org.qubership.integration.platform.engine.errorhandling.ChainExecutionTimeoutException; import org.qubership.integration.platform.engine.errorhandling.ResponseValidationException; import org.qubership.integration.platform.engine.errorhandling.errorcode.ErrorCode; import org.qubership.integration.platform.engine.errorhandling.ValidationException; @@ -112,6 +114,16 @@ public void handleException(IterationLimitException exception, Exchange exchange makeExceptionResponseInExchange(exchange, errorCode, extraParameters); } + @ChainExceptionHandler(value = ChainExecutionTimeoutException.class, errorCode = ErrorCode.TIMEOUT_REACHED) + public void handleException(ChainExecutionTimeoutException exception, Exchange exchange, ErrorCode errorCode, Map extraParameters) throws IOException { + makeExceptionResponseInExchange(exchange, errorCode, extraParameters); + } + + @ChainExceptionHandler(value = ChainExecutionTerminatedException.class, errorCode = ErrorCode.FORCE_TERMINATED) + public void handleException(ChainExecutionTerminatedException exception, Exchange exchange, ErrorCode errorCode, Map extraParameters) throws IOException { + makeExceptionResponseInExchange(exchange, errorCode, extraParameters); + } + private void makeExceptionResponseInExchange(Exchange exchange, ErrorCode errorCode, Map extraParameters) throws IOException { exchange.getMessage().removeHeaders("*"); exchange.getMessage().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); diff --git a/src/main/java/org/qubership/integration/platform/engine/camel/processors/HttpTriggerFinishProcessor.java b/src/main/java/org/qubership/integration/platform/engine/camel/processors/HttpTriggerFinishProcessor.java index e313a3a..16473b0 100644 --- a/src/main/java/org/qubership/integration/platform/engine/camel/processors/HttpTriggerFinishProcessor.java +++ b/src/main/java/org/qubership/integration/platform/engine/camel/processors/HttpTriggerFinishProcessor.java @@ -84,11 +84,9 @@ private void logHttpTriggerRequestFinished(Exchange exchange, CamelDebuggerPrope return; // Log only if it is info level OR session is failed } - String started = exchange.getProperty(CamelConstants.Properties.START_TIME, - String.class); - String finished = LocalDateTime.now().toString(); - long duration = Duration.between(LocalDateTime.parse(started), - LocalDateTime.parse(finished)).toMillis(); + long started = exchange.getProperty(CamelConstants.Properties.START_TIME_MS, + Long.class); + long duration = System.currentTimeMillis() - started; List messageHistory = (List) exchange.getAllProperties() .getOrDefault(Exchange.MESSAGE_HISTORY, Collections.emptyList()); diff --git a/src/main/java/org/qubership/integration/platform/engine/camel/processors/SplitAsyncProcessor.java b/src/main/java/org/qubership/integration/platform/engine/camel/processors/SplitAsyncProcessor.java new file mode 100644 index 0000000..a6607ca --- /dev/null +++ b/src/main/java/org/qubership/integration/platform/engine/camel/processors/SplitAsyncProcessor.java @@ -0,0 +1,32 @@ +/* + * Copyright 2024-2025 NetCracker Technology Corporation + * + * 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. + */ + +package org.qubership.integration.platform.engine.camel.processors; + +import lombok.extern.slf4j.Slf4j; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.qubership.integration.platform.engine.model.constants.CamelConstants.Properties; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class SplitAsyncProcessor implements Processor { + @Override + public void process(Exchange exchange) throws Exception { + exchange.removeProperty(Properties.CHAIN_TIME_OUT_AFTER); + } +} diff --git a/src/main/java/org/qubership/integration/platform/engine/errorhandling/ChainExecutionTerminatedException.java b/src/main/java/org/qubership/integration/platform/engine/errorhandling/ChainExecutionTerminatedException.java new file mode 100644 index 0000000..5503c94 --- /dev/null +++ b/src/main/java/org/qubership/integration/platform/engine/errorhandling/ChainExecutionTerminatedException.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024-2025 NetCracker Technology Corporation + * + * 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. + */ + +package org.qubership.integration.platform.engine.errorhandling; + +public class ChainExecutionTerminatedException extends RuntimeException { + public ChainExecutionTerminatedException(String message) { + super(message); + } +} diff --git a/src/main/java/org/qubership/integration/platform/engine/errorhandling/ChainExecutionTimeoutException.java b/src/main/java/org/qubership/integration/platform/engine/errorhandling/ChainExecutionTimeoutException.java new file mode 100644 index 0000000..78c6ab0 --- /dev/null +++ b/src/main/java/org/qubership/integration/platform/engine/errorhandling/ChainExecutionTimeoutException.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024-2025 NetCracker Technology Corporation + * + * 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. + */ + +package org.qubership.integration.platform.engine.errorhandling; + +public class ChainExecutionTimeoutException extends RuntimeException { + public ChainExecutionTimeoutException(String message) { + super(message); + } +} diff --git a/src/main/java/org/qubership/integration/platform/engine/errorhandling/errorcode/ErrorCode.java b/src/main/java/org/qubership/integration/platform/engine/errorhandling/errorcode/ErrorCode.java index ce4afeb..7725638 100644 --- a/src/main/java/org/qubership/integration/platform/engine/errorhandling/errorcode/ErrorCode.java +++ b/src/main/java/org/qubership/integration/platform/engine/errorhandling/errorcode/ErrorCode.java @@ -55,6 +55,8 @@ public enum ErrorCode { RESPONSE_VALIDATION_ERROR("0112", HttpStatus.SC_INTERNAL_SERVER_ERROR, "Response captured by one of the service calls has not passed configured validations", "Chain failed due to receiving unexpected response from service"), + TIMEOUT_REACHED("0113", HttpStatus.SC_INTERNAL_SERVER_ERROR, "Chain timeout reached","Chain execution failed due to reaching execution timeout"), + FORCE_TERMINATED("0114", HttpStatus.SC_INTERNAL_SERVER_ERROR, "Chain session was shut down","Chain execution was stopped manually"), // Deployment errors diff --git a/src/main/java/org/qubership/integration/platform/engine/model/constants/CamelConstants.java b/src/main/java/org/qubership/integration/platform/engine/model/constants/CamelConstants.java index 534f68f..fa74e03 100644 --- a/src/main/java/org/qubership/integration/platform/engine/model/constants/CamelConstants.java +++ b/src/main/java/org/qubership/integration/platform/engine/model/constants/CamelConstants.java @@ -70,6 +70,9 @@ public final static class Properties { public static final String STEPS = INTERNAL_PROPERTY_PREFIX + "steps"; public static final String EXCHANGES = INTERNAL_PROPERTY_PREFIX + "exchanges"; public static final String START_TIME = INTERNAL_PROPERTY_PREFIX + "startTime"; + public static final String START_TIME_MS = INTERNAL_PROPERTY_PREFIX + "startTimeMs"; + public static final String EXCHANGE_START_TIME_MS = INTERNAL_PROPERTY_PREFIX + "exchangeStartTimeMs"; + public static final String IS_MAIN_EXCHANGE = INTERNAL_PROPERTY_PREFIX + "isMainExchange"; public static final String ELEMENT_FAILED = INTERNAL_PROPERTY_PREFIX + "elementFailed"; public static final String LAST_EXCEPTION = INTERNAL_PROPERTY_PREFIX + "lastException"; public static final String LAST_EXCEPTION_ERROR_CODE = INTERNAL_PROPERTY_PREFIX + "laseExceptionErrorCode"; @@ -148,6 +151,8 @@ public final static class Properties { public static final String CORRELATION_ID_POSITION = "correlationIdPosition"; public static final String CORRELATION_ID_NAME = "correlationIdName"; public static final String IS_CHECKPOINT_TRIGGER_STEP = INTERNAL_PROPERTY_PREFIX + "isCheckpointTriggerStep"; + public static final String CHAIN_TIMED_OUT = INTERNAL_PROPERTY_PREFIX + "chainSessionTimedOut"; + public static final String CHAIN_TIME_OUT_AFTER = INTERNAL_PROPERTY_PREFIX + "chainSessionTimeoutAfter"; public static final String HTTP_TRIGGER_STEP_ID = "httpTriggerStepId"; diff --git a/src/main/java/org/qubership/integration/platform/engine/rest/v1/controller/LiveExchangesController.java b/src/main/java/org/qubership/integration/platform/engine/rest/v1/controller/LiveExchangesController.java new file mode 100644 index 0000000..32a45c3 --- /dev/null +++ b/src/main/java/org/qubership/integration/platform/engine/rest/v1/controller/LiveExchangesController.java @@ -0,0 +1,70 @@ +/* + * Copyright 2024-2025 NetCracker Technology Corporation + * + * 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. + */ + +package org.qubership.integration.platform.engine.rest.v1.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import lombok.extern.slf4j.Slf4j; +import org.qubership.integration.platform.engine.rest.v1.dto.LiveExchangeDTO; +import org.qubership.integration.platform.engine.service.LiveExchangesService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.CollectionUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Slf4j +@RestController +@Validated +@RequestMapping( + value = "/v1/engine/live-exchanges", + produces = MediaType.APPLICATION_JSON_VALUE +) +@Tag(name = "live-exchanges-controller", description = "Live Exchanges Controller") +public class LiveExchangesController { + private final LiveExchangesService liveExchangesService; + + @Autowired + public LiveExchangesController(LiveExchangesService liveExchangesService) { + this.liveExchangesService = liveExchangesService; + } + + @GetMapping + @Operation(description = "Get top N running exchanges ordered by execution time DESC") + public ResponseEntity> getLiveExchanges( + @RequestParam(required = false, defaultValue = "10") @Positive @Parameter(description = "Amount of exchanges to view") Integer limit) { + List result = liveExchangesService.getTopLiveExchanges(limit); + if (CollectionUtils.isEmpty(result)) { + return ResponseEntity.noContent().build(); + } + return ResponseEntity.ok(result); + } + + @DeleteMapping("/{deploymentId}/{exchangeId}") + @Operation(description = "Try to kill specified exchange") + public ResponseEntity killExchange(@PathVariable @NotBlank @Parameter(description = "Deployment ID") String deploymentId, + @PathVariable @NotBlank @Parameter(description = "Exchange ID") String exchangeId) { + liveExchangesService.killLiveExchangeById(deploymentId, exchangeId); + return ResponseEntity.accepted().build(); + } +} diff --git a/src/main/java/org/qubership/integration/platform/engine/rest/v1/dto/LiveExchangeDTO.java b/src/main/java/org/qubership/integration/platform/engine/rest/v1/dto/LiveExchangeDTO.java new file mode 100644 index 0000000..a9eb15b --- /dev/null +++ b/src/main/java/org/qubership/integration/platform/engine/rest/v1/dto/LiveExchangeDTO.java @@ -0,0 +1,30 @@ +package org.qubership.integration.platform.engine.rest.v1.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; +import org.qubership.integration.platform.engine.model.logging.SessionsLoggingLevel; + +@Data +@Builder +@Schema(description = "Information about Live exchange") +public class LiveExchangeDTO { + @Schema(description = "Exchange id") + private String exchangeId; + @Schema(description = "Deployment id") + private String deploymentId; + @Schema(description = "Session id") + private String sessionId; + @Schema(description = "Chain id") + protected String chainId; + @Schema(description = "Duration of current exchange, in ms") + private Long duration; + @Schema(description = "Duration of the whole session exchange participates in, in ms") + private Long sessionDuration; + @Schema(description = "Session start timestamp") + private Long sessionStartTime; + @Schema(description = "Current session log level") + private SessionsLoggingLevel sessionLogLevel; + @Schema(description = "Is current exchange main (initial)") + private Boolean main; +} diff --git a/src/main/java/org/qubership/integration/platform/engine/service/IntegrationRuntimeService.java b/src/main/java/org/qubership/integration/platform/engine/service/IntegrationRuntimeService.java index bec7bf5..9f43e0a 100644 --- a/src/main/java/org/qubership/integration/platform/engine/service/IntegrationRuntimeService.java +++ b/src/main/java/org/qubership/integration/platform/engine/service/IntegrationRuntimeService.java @@ -556,6 +556,7 @@ private SpringCamelContext buildContext( applicationContext.getBean(SecurityAccessPolicyConverter.class)); context.getGlobalOptions().put(JacksonConstants.ENABLE_TYPE_CONVERTER, "true"); context.getGlobalOptions().put(JacksonConstants.TYPE_CONVERTER_TO_POJO, "true"); + context.getInflightRepository().setInflightBrowseEnabled(true); boolean deploymentsSuspended = isDeploymentsSuspended(); if (deploymentsSuspended) { @@ -740,7 +741,7 @@ private void removeRetryingDeployment(String deploymentId) { getCache().removeRetryDeploymentFromQueue(deploymentId); } - private RuntimeIntegrationCache getCache() { + public RuntimeIntegrationCache getCache() { return deploymentCache; } diff --git a/src/main/java/org/qubership/integration/platform/engine/service/LiveExchangesService.java b/src/main/java/org/qubership/integration/platform/engine/service/LiveExchangesService.java new file mode 100644 index 0000000..7767892 --- /dev/null +++ b/src/main/java/org/qubership/integration/platform/engine/service/LiveExchangesService.java @@ -0,0 +1,113 @@ +/* + * Copyright 2024-2025 NetCracker Technology Corporation + * + * 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. + */ + +package org.qubership.integration.platform.engine.service; + +import com.google.common.collect.MinMaxPriorityQueue; +import jakarta.persistence.EntityNotFoundException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.camel.Exchange; +import org.apache.camel.spi.InflightRepository; +import org.apache.camel.spring.SpringCamelContext; +import org.qubership.integration.platform.engine.errorhandling.ChainExecutionTerminatedException; +import org.qubership.integration.platform.engine.model.constants.CamelConstants; +import org.qubership.integration.platform.engine.model.deployment.properties.CamelDebuggerProperties; +import org.qubership.integration.platform.engine.rest.v1.dto.LiveExchangeDTO; +import org.qubership.integration.platform.engine.service.debugger.CamelDebuggerPropertiesService; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class LiveExchangesService { + + Comparator EXCHANGE_COMPARATOR = + Comparator.comparingLong(holder -> holder.getInflightExchange().getDuration() * -1); + + @RequiredArgsConstructor + @Getter + private static class InflightExchangeHolder { + private final InflightRepository.InflightExchange inflightExchange; + private final String deploymentId; + } + + private final IntegrationRuntimeService integrationRuntimeService; + private final CamelDebuggerPropertiesService propertiesService; + + public LiveExchangesService(IntegrationRuntimeService integrationRuntimeService, CamelDebuggerPropertiesService propertiesService) { + this.integrationRuntimeService = integrationRuntimeService; + this.propertiesService = propertiesService; + } + + public List getTopLiveExchanges(int amount) { + List result = new ArrayList<>(); + MinMaxPriorityQueue inflightExchanges = MinMaxPriorityQueue + .orderedBy(EXCHANGE_COMPARATOR).maximumSize(amount).create(); + + for (Map.Entry entry : integrationRuntimeService.getCache().getContexts().entrySet()) { + String deploymentId = entry.getKey(); + SpringCamelContext context = entry.getValue(); + List exchangeHolders = context.getInflightRepository().browse(amount, true).stream() + .map(ex -> new InflightExchangeHolder(ex, deploymentId)).toList(); + inflightExchanges.addAll(exchangeHolders); + } + + for (InflightExchangeHolder exchangeHolder : inflightExchanges) { + Exchange exchange = exchangeHolder.getInflightExchange().getExchange(); + Long sessionStartTime = exchange.getProperty(CamelConstants.Properties.START_TIME_MS, Long.class); + Long sessionDuration = sessionStartTime == null ? null : System.currentTimeMillis() - sessionStartTime; + Long exchangeStartTime = exchange.getProperty(CamelConstants.Properties.EXCHANGE_START_TIME_MS, Long.class); + Long exchangeDuration = exchangeStartTime == null ? null : System.currentTimeMillis() - exchangeStartTime; + CamelDebuggerProperties properties = propertiesService.getProperties(exchange, exchangeHolder.getDeploymentId()); + String chainId = properties.getDeploymentInfo().getChainId(); + result.add(LiveExchangeDTO.builder() + .exchangeId(exchange.getExchangeId()) + .deploymentId(exchangeHolder.getDeploymentId()) + .sessionId(exchange.getProperty(CamelConstants.Properties.SESSION_ID, String.class)) + .chainId(chainId) + .sessionStartTime(sessionStartTime) + .sessionDuration(sessionDuration) + .sessionLogLevel(properties.getActualRuntimeProperties().calculateSessionLevel(exchange)) + .duration(exchangeDuration) + .main(exchange.getProperty(CamelConstants.Properties.IS_MAIN_EXCHANGE, Boolean.class)) + .build()); + } + + return result; + } + + public void killLiveExchangeById(String deploymentId, String exchangeId) { + SpringCamelContext context = integrationRuntimeService.getCache().getContexts().get(deploymentId); + if (context == null) { + throw new EntityNotFoundException("No deployment found for id " + deploymentId); + } + + Exchange exchange = context.getInflightRepository().browse().stream() + .filter(inflightExchange -> exchangeId.equals(inflightExchange.getExchange().getExchangeId())) + .findAny().orElseThrow(() -> new EntityNotFoundException("No live exchange found for deployment id " + deploymentId)) + .getExchange(); + + exchange.setException(new ChainExecutionTerminatedException("Chain was interrupted manually")); + } + + +} \ No newline at end of file diff --git a/src/main/java/org/qubership/integration/platform/engine/service/debugger/CamelDebugger.java b/src/main/java/org/qubership/integration/platform/engine/service/debugger/CamelDebugger.java index 5cc4fe7..6af4e42 100644 --- a/src/main/java/org/qubership/integration/platform/engine/service/debugger/CamelDebugger.java +++ b/src/main/java/org/qubership/integration/platform/engine/service/debugger/CamelDebugger.java @@ -18,6 +18,7 @@ import org.qubership.integration.platform.engine.camel.context.propagation.CamelExchangeContextPropagation; import org.qubership.integration.platform.engine.configuration.ServerConfiguration; +import org.qubership.integration.platform.engine.errorhandling.ChainExecutionTimeoutException; import org.qubership.integration.platform.engine.errorhandling.errorcode.ErrorCode; import org.qubership.integration.platform.engine.model.ChainElementType; import org.qubership.integration.platform.engine.model.Session; @@ -30,7 +31,6 @@ import org.qubership.integration.platform.engine.model.logging.SessionsLoggingLevel; import org.qubership.integration.platform.engine.persistence.shared.entity.Checkpoint; import org.qubership.integration.platform.engine.persistence.shared.entity.SessionInfo; -import org.qubership.integration.platform.engine.rest.v1.controller.CheckpointSessionController; import org.qubership.integration.platform.engine.service.CheckpointSessionService; import org.qubership.integration.platform.engine.service.ExecutionStatus; import org.qubership.integration.platform.engine.service.VariablesService; @@ -248,6 +248,8 @@ public boolean afterProcess(Exchange exchange, Processor processor, NamedNode de long timeTaken) { CamelDebuggerProperties dbgProperties = getRelatedProperties(exchange); + checkExecutionTimeout(exchange); + initOrActivatePropagatedContext(exchange); SessionsLoggingLevel actualSessionLevel = dbgProperties.getRuntimeProperties(exchange) @@ -361,11 +363,13 @@ private void exchangeStarted(Exchange exchange, CamelDebuggerProperties dbgPrope if (sessionId == null) { sessionId = UUID.randomUUID().toString(); String started = LocalDateTime.now().toString(); + Long startedMillis = System.currentTimeMillis(); exchange.setProperty(CamelConstants.Properties.SESSION_ID, sessionId); exchange.setProperty(CamelConstants.Properties.SESSION_SHOULD_BE_LOGGED, sessionsService.sessionShouldBeLogged()); exchange.setProperty(CamelConstants.Properties.START_TIME, started); + exchange.setProperty(CamelConstants.Properties.START_TIME_MS, startedMillis); exchange.getProperty(CamelConstants.Properties.EXCHANGES, ConcurrentHashMap.class) .put(sessionId, new ConcurrentHashMap()); @@ -752,4 +756,21 @@ private void setFailedElementId(Exchange exchange, Map elementPr DebuggerUtils.setOverallWarning(exchange, false); } } + + private void checkExecutionTimeout(Exchange exchange) { + long timeoutAfter = exchange.getProperty(CamelConstants.Properties.CHAIN_TIME_OUT_AFTER, 0, Long.class); + if (timeoutAfter <= 0) { + return; + } + long startTime = exchange.getProperty(CamelConstants.Properties.START_TIME_MS, Long.class); + long duration = System.currentTimeMillis() - startTime; + boolean isTimedOut = exchange.getProperty(CamelConstants.Properties.CHAIN_TIMED_OUT, false, Boolean.class); + + if (duration > timeoutAfter && !isTimedOut) { + Exception exception = new ChainExecutionTimeoutException("Chain execution timed out after " + duration + + " ms. Desired limit is " + timeoutAfter + " ms."); + exchange.setProperty(CamelConstants.Properties.CHAIN_TIMED_OUT, true); + exchange.setException(exception); + } + } } diff --git a/src/main/java/org/qubership/integration/platform/engine/service/debugger/util/DebuggerUtils.java b/src/main/java/org/qubership/integration/platform/engine/service/debugger/util/DebuggerUtils.java index 40bcd47..b26ee69 100644 --- a/src/main/java/org/qubership/integration/platform/engine/service/debugger/util/DebuggerUtils.java +++ b/src/main/java/org/qubership/integration/platform/engine/service/debugger/util/DebuggerUtils.java @@ -109,6 +109,11 @@ public static void initInternalExchangeVariables(Exchange exchange) { new ConcurrentHashMap<>() : new ConcurrentHashMap<>(exchange.getProperty( Properties.EXCHANGES, ConcurrentHashMap.class))); + + exchange.setProperty(Properties.IS_MAIN_EXCHANGE, + exchange.getProperty(Properties.IS_MAIN_EXCHANGE) == null + ); + exchange.setProperty(Properties.EXCHANGE_START_TIME_MS, System.currentTimeMillis()); } public static String getNodeIdForExecutionMap(String nodeId, String splitId) {