From 62a066ccd5f31575d34f120ed5b16d93f95faf0a Mon Sep 17 00:00:00 2001 From: shangeyao Date: Fri, 7 Aug 2026 11:03:32 +0800 Subject: [PATCH] [E2E] Migrate tests to REST API and expand Flink version matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Selenium UI E2E with API integration tests via StreamParkApiExtension and FlinkApiSupport. Cover Flink 1.17–2.3 on Remote/Yarn/K8s, remove 1.16, and update CI to 52 api-e2e jobs without ui-e2e. Co-authored-by: Cursor --- .github/workflows/e2e.yml | 130 +++-- streampark-e2e/pom.xml | 7 + streampark-e2e/scripts/generate-flink-e2e.sh | 484 ++++++++++++++++++ .../streampark/e2e/cases/AlarmTest.java | 167 +++--- .../streampark/e2e/cases/EnvironmentTest.java | 219 +++----- .../e2e/cases/ExternalLinkTest.java | 130 ++--- .../Flink116OnRemoteClusterDeployTest.java | 104 ---- .../Flink116OnYarnClusterDeployTest.java | 151 ------ .../cases/Flink117OnK8sClusterDeployTest.java | 113 ++++ .../Flink117OnRemoteClusterDeployTest.java | 80 +-- .../Flink117OnYarnClusterDeployTest.java | 125 ++--- .../cases/Flink118OnK8sClusterDeployTest.java | 113 ++++ .../Flink118OnRemoteClusterDeployTest.java | 80 +-- .../Flink118OnYarnClusterDeployTest.java | 125 ++--- .../cases/Flink119OnK8sClusterDeployTest.java | 113 ++++ .../Flink119OnRemoteClusterDeployTest.java | 76 +++ .../Flink119OnYarnClusterDeployTest.java | 105 ++++ .../cases/Flink120OnK8sClusterDeployTest.java | 113 ++++ .../Flink120OnRemoteClusterDeployTest.java | 80 +-- .../Flink120OnYarnClusterDeployTest.java | 125 ++--- .../cases/Flink200OnK8sClusterDeployTest.java | 113 ++++ .../Flink200OnRemoteClusterDeployTest.java | 76 +++ .../Flink200OnYarnClusterDeployTest.java | 105 ++++ .../cases/Flink210OnK8sClusterDeployTest.java | 113 ++++ .../Flink210OnRemoteClusterDeployTest.java | 76 +++ .../Flink210OnYarnClusterDeployTest.java | 105 ++++ .../cases/Flink220OnK8sClusterDeployTest.java | 113 ++++ .../Flink220OnRemoteClusterDeployTest.java | 76 +++ .../Flink220OnYarnClusterDeployTest.java | 105 ++++ .../cases/Flink230OnK8sClusterDeployTest.java | 113 ++++ .../Flink230OnRemoteClusterDeployTest.java | 76 +++ .../Flink230OnYarnClusterDeployTest.java | 105 ++++ .../e2e/cases/FlinkSQL116OnYarnTest.java | 330 ------------ .../e2e/cases/FlinkSQL117OnK8sTest.java | 168 ++++++ .../e2e/cases/FlinkSQL117OnYarnTest.java | 341 +++--------- .../e2e/cases/FlinkSQL118OnK8sTest.java | 168 ++++++ .../e2e/cases/FlinkSQL118OnYarnTest.java | 353 +++---------- .../e2e/cases/FlinkSQL119OnK8sTest.java | 168 ++++++ .../e2e/cases/FlinkSQL119OnYarnTest.java | 160 ++++++ .../e2e/cases/FlinkSQL120OnK8sTest.java | 168 ++++++ .../e2e/cases/FlinkSQL120OnYarnTest.java | 257 +++------- .../e2e/cases/FlinkSQL200OnK8sTest.java | 168 ++++++ .../e2e/cases/FlinkSQL200OnYarnTest.java | 160 ++++++ .../e2e/cases/FlinkSQL210OnK8sTest.java | 168 ++++++ .../e2e/cases/FlinkSQL210OnYarnTest.java | 160 ++++++ .../e2e/cases/FlinkSQL220OnK8sTest.java | 168 ++++++ .../e2e/cases/FlinkSQL220OnYarnTest.java | 160 ++++++ .../e2e/cases/FlinkSQL230OnK8sTest.java | 168 ++++++ .../e2e/cases/FlinkSQL230OnYarnTest.java | 160 ++++++ .../e2e/cases/MemberManagementTest.java | 108 ++-- .../e2e/cases/ProjectsManagementTest.java | 122 +++-- .../e2e/cases/RoleManagementTest.java | 95 ++-- .../e2e/cases/TeamManagementTest.java | 96 ++-- .../e2e/cases/TokenManagementTest.java | 100 ++-- .../e2e/cases/UploadManagementTest.java | 157 +++--- .../e2e/cases/UserManagementTest.java | 104 ++-- .../e2e/cases/VariableManagementTest.java | 115 +++-- .../streampark/e2e/cases/YarnQueueTest.java | 107 ++-- .../flink/applications/ApplicationForm.java | 37 ++ .../flink/clusters/ClusterDetailForm.java | 8 + .../flink/clusters/KubernetesSessionForm.java | 89 ++++ .../docker/flink-1.17-on-k8s/Dockerfile | 21 + .../flink-1.17-on-k8s/docker-compose.yaml | 78 +++ .../docker/flink-1.18-on-k8s/Dockerfile | 21 + .../flink-1.18-on-k8s/docker-compose.yaml | 78 +++ .../docker/flink-1.19-on-k8s/Dockerfile | 21 + .../flink-1.19-on-k8s/docker-compose.yaml | 78 +++ .../Dockerfile | 0 .../docker-compose.yaml | 4 +- .../Dockerfile | 11 +- .../docker-compose.config | 0 .../docker-compose.yaml | 26 +- .../docker/flink-1.20-on-k8s/Dockerfile | 21 + .../flink-1.20-on-k8s/docker-compose.yaml | 78 +++ .../docker/flink-2.0-on-k8s/Dockerfile | 21 + .../flink-2.0-on-k8s/docker-compose.yaml | 78 +++ .../docker/flink-2.0-on-remote/Dockerfile | 18 + .../flink-2.0-on-remote/docker-compose.yaml | 90 ++++ .../docker/flink-2.0-on-yarn/Dockerfile | 32 ++ .../flink-2.0-on-yarn/docker-compose.config | 44 ++ .../flink-2.0-on-yarn/docker-compose.yaml | 163 ++++++ .../docker/flink-2.1-on-k8s/Dockerfile | 21 + .../flink-2.1-on-k8s/docker-compose.yaml | 78 +++ .../docker/flink-2.1-on-remote/Dockerfile | 18 + .../flink-2.1-on-remote/docker-compose.yaml | 90 ++++ .../docker/flink-2.1-on-yarn/Dockerfile | 32 ++ .../flink-2.1-on-yarn/docker-compose.config | 44 ++ .../flink-2.1-on-yarn/docker-compose.yaml | 163 ++++++ .../docker/flink-2.2-on-k8s/Dockerfile | 21 + .../flink-2.2-on-k8s/docker-compose.yaml | 78 +++ .../docker/flink-2.2-on-remote/Dockerfile | 18 + .../flink-2.2-on-remote/docker-compose.yaml | 90 ++++ .../docker/flink-2.2-on-yarn/Dockerfile | 32 ++ .../flink-2.2-on-yarn/docker-compose.config | 44 ++ .../flink-2.2-on-yarn/docker-compose.yaml | 163 ++++++ .../docker/flink-2.3-on-k8s/Dockerfile | 21 + .../flink-2.3-on-k8s/docker-compose.yaml | 78 +++ .../docker/flink-2.3-on-remote/Dockerfile | 18 + .../flink-2.3-on-remote/docker-compose.yaml | 90 ++++ .../docker/flink-2.3-on-yarn/Dockerfile | 32 ++ .../flink-2.3-on-yarn/docker-compose.config | 44 ++ .../flink-2.3-on-yarn/docker-compose.yaml | 163 ++++++ .../streampark/e2e/core/StreamParkApi.java | 41 ++ .../e2e/core/StreamParkApiExtension.java | 124 +++++ .../streampark/e2e/core/api/ApiClient.java | 269 ++++++++++ .../streampark/e2e/core/api/ApiResponse.java | 52 ++ .../e2e/core/api/flink/FlinkApiConstants.java | 75 +++ .../e2e/core/api/flink/FlinkApiSupport.java | 300 +++++++++++ 108 files changed, 8854 insertions(+), 2607 deletions(-) create mode 100755 streampark-e2e/scripts/generate-flink-e2e.sh delete mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink116OnRemoteClusterDeployTest.java delete mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink116OnYarnClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnK8sClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnK8sClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnK8sClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnRemoteClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnYarnClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnK8sClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnK8sClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnRemoteClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnYarnClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnK8sClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnRemoteClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnYarnClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnK8sClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnRemoteClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnYarnClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnK8sClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnRemoteClusterDeployTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnYarnClusterDeployTest.java delete mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL116OnYarnTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL117OnK8sTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL118OnK8sTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL119OnK8sTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL119OnYarnTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL120OnK8sTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL200OnK8sTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL200OnYarnTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL210OnK8sTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL210OnYarnTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL220OnK8sTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL220OnYarnTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL230OnK8sTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL230OnYarnTest.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/clusters/KubernetesSessionForm.java create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.17-on-k8s/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.17-on-k8s/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.18-on-k8s/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.18-on-k8s/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-k8s/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-k8s/docker-compose.yaml rename streampark-e2e/streampark-e2e-case/src/test/resources/docker/{flink-1.16-on-remote => flink-1.19-on-remote}/Dockerfile (100%) rename streampark-e2e/streampark-e2e-case/src/test/resources/docker/{flink-1.16-on-remote => flink-1.19-on-remote}/docker-compose.yaml (98%) rename streampark-e2e/streampark-e2e-case/src/test/resources/docker/{flink-1.16-on-yarn => flink-1.19-on-yarn}/Dockerfile (78%) rename streampark-e2e/streampark-e2e-case/src/test/resources/docker/{flink-1.16-on-yarn => flink-1.19-on-yarn}/docker-compose.config (100%) rename streampark-e2e/streampark-e2e-case/src/test/resources/docker/{flink-1.16-on-yarn => flink-1.19-on-yarn}/docker-compose.yaml (87%) create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.20-on-k8s/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.20-on-k8s/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-k8s/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-k8s/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-remote/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-remote/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/docker-compose.config create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-k8s/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-k8s/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-remote/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-remote/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/docker-compose.config create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-k8s/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-k8s/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-remote/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-remote/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/docker-compose.config create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-k8s/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-k8s/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-remote/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-remote/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/Dockerfile create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/docker-compose.config create mode 100644 streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/docker-compose.yaml create mode 100644 streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/StreamParkApi.java create mode 100644 streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/StreamParkApiExtension.java create mode 100644 streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/ApiClient.java create mode 100644 streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/ApiResponse.java create mode 100644 streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/flink/FlinkApiConstants.java create mode 100644 streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/flink/FlinkApiSupport.java diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d2108163b8..f3dd903420 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -27,7 +27,7 @@ name: E2E concurrency: group: e2e-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true + cancel-in-progress: false jobs: @@ -102,12 +102,13 @@ jobs: name: standalone-image path: /tmp/standalone-image.tar retention-days: 1 - e2e: - name: ${{ matrix.case.name }} + api-e2e: + name: API-${{ matrix.case.name }} needs: build runs-on: ubuntu-latest - timeout-minutes: 180 + timeout-minutes: 120 strategy: + fail-fast: false matrix: case: - name: EnvironmentTest @@ -135,34 +136,98 @@ jobs: - name: VariableManagementTest class: org.apache.streampark.e2e.cases.VariableManagementTest - - name: FlinkOnRemoteClusterDeployTest + # Flink remote cluster (API) + - name: Flink117-RemoteDeploy + class: org.apache.streampark.e2e.cases.Flink117OnRemoteClusterDeployTest + - name: Flink118-RemoteDeploy + class: org.apache.streampark.e2e.cases.Flink118OnRemoteClusterDeployTest + - name: Flink119-RemoteDeploy + class: org.apache.streampark.e2e.cases.Flink119OnRemoteClusterDeployTest + - name: Flink120-RemoteDeploy class: org.apache.streampark.e2e.cases.Flink120OnRemoteClusterDeployTest + - name: Flink200-RemoteDeploy + class: org.apache.streampark.e2e.cases.Flink200OnRemoteClusterDeployTest + - name: Flink210-RemoteDeploy + class: org.apache.streampark.e2e.cases.Flink210OnRemoteClusterDeployTest + - name: Flink220-RemoteDeploy + class: org.apache.streampark.e2e.cases.Flink220OnRemoteClusterDeployTest + - name: Flink230-RemoteDeploy + class: org.apache.streampark.e2e.cases.Flink230OnRemoteClusterDeployTest - # - name: FlinkOnYarnClusterDeployTest - # class: org.apache.streampark.e2e.cases.Flink120OnYarnClusterDeployTest + # Flink yarn cluster (API) + - name: Flink117-YarnClusterDeploy + class: org.apache.streampark.e2e.cases.Flink117OnYarnClusterDeployTest + - name: Flink118-YarnClusterDeploy + class: org.apache.streampark.e2e.cases.Flink118OnYarnClusterDeployTest + - name: Flink119-YarnClusterDeploy + class: org.apache.streampark.e2e.cases.Flink119OnYarnClusterDeployTest + - name: Flink120-YarnClusterDeploy + class: org.apache.streampark.e2e.cases.Flink120OnYarnClusterDeployTest + - name: Flink200-YarnClusterDeploy + class: org.apache.streampark.e2e.cases.Flink200OnYarnClusterDeployTest + - name: Flink210-YarnClusterDeploy + class: org.apache.streampark.e2e.cases.Flink210OnYarnClusterDeployTest + - name: Flink220-YarnClusterDeploy + class: org.apache.streampark.e2e.cases.Flink220OnYarnClusterDeployTest + - name: Flink230-YarnClusterDeploy + class: org.apache.streampark.e2e.cases.Flink230OnYarnClusterDeployTest - # - name: FlinkSQLOnYarnTest - # class: org.apache.streampark.e2e.cases.FlinkSQL120OnYarnTest + # Flink SQL on Yarn (API) + - name: FlinkSQL117-Yarn + class: org.apache.streampark.e2e.cases.FlinkSQL117OnYarnTest + - name: FlinkSQL118-Yarn + class: org.apache.streampark.e2e.cases.FlinkSQL118OnYarnTest + - name: FlinkSQL119-Yarn + class: org.apache.streampark.e2e.cases.FlinkSQL119OnYarnTest + - name: FlinkSQL120-Yarn + class: org.apache.streampark.e2e.cases.FlinkSQL120OnYarnTest + - name: FlinkSQL200-Yarn + class: org.apache.streampark.e2e.cases.FlinkSQL200OnYarnTest + - name: FlinkSQL210-Yarn + class: org.apache.streampark.e2e.cases.FlinkSQL210OnYarnTest + - name: FlinkSQL220-Yarn + class: org.apache.streampark.e2e.cases.FlinkSQL220OnYarnTest + - name: FlinkSQL230-Yarn + class: org.apache.streampark.e2e.cases.FlinkSQL230OnYarnTest - #- name: Flink117OnRemoteClusterDeployTest - # class: org.apache.streampark.e2e.cases.Flink117OnRemoteClusterDeployTest - #- name: Flink116OnRemoteClusterDeployTest - # class: org.apache.streampark.e2e.cases.Flink116OnRemoteClusterDeployTest + # Flink K8s cluster (API) + - name: Flink117-K8sClusterDeploy + class: org.apache.streampark.e2e.cases.Flink117OnK8sClusterDeployTest + - name: Flink118-K8sClusterDeploy + class: org.apache.streampark.e2e.cases.Flink118OnK8sClusterDeployTest + - name: Flink119-K8sClusterDeploy + class: org.apache.streampark.e2e.cases.Flink119OnK8sClusterDeployTest + - name: Flink120-K8sClusterDeploy + class: org.apache.streampark.e2e.cases.Flink120OnK8sClusterDeployTest + - name: Flink200-K8sClusterDeploy + class: org.apache.streampark.e2e.cases.Flink200OnK8sClusterDeployTest + - name: Flink210-K8sClusterDeploy + class: org.apache.streampark.e2e.cases.Flink210OnK8sClusterDeployTest + - name: Flink220-K8sClusterDeploy + class: org.apache.streampark.e2e.cases.Flink220OnK8sClusterDeployTest + - name: Flink230-K8sClusterDeploy + class: org.apache.streampark.e2e.cases.Flink230OnK8sClusterDeployTest - # - name: Flink116OnYarnClusterDeployTest - # class: org.apache.streampark.e2e.cases.Flink116OnYarnClusterDeployTest - # - name: Flink117OnYarnClusterDeployTest - # class: org.apache.streampark.e2e.cases.Flink117OnYarnClusterDeployTest - - # - name: FlinkSQL116OnYarnTest - # class: org.apache.streampark.e2e.cases.FlinkSQL116OnYarnTest - # - name: FlinkSQL117OnYarnTest - # class: org.apache.streampark.e2e.cases.FlinkSQL117OnYarnTest - env: - RECORDING_PATH: /tmp/recording-${{ matrix.case.name }} + # Flink SQL on K8s (API) + - name: FlinkSQL117-K8s + class: org.apache.streampark.e2e.cases.FlinkSQL117OnK8sTest + - name: FlinkSQL118-K8s + class: org.apache.streampark.e2e.cases.FlinkSQL118OnK8sTest + - name: FlinkSQL119-K8s + class: org.apache.streampark.e2e.cases.FlinkSQL119OnK8sTest + - name: FlinkSQL120-K8s + class: org.apache.streampark.e2e.cases.FlinkSQL120OnK8sTest + - name: FlinkSQL200-K8s + class: org.apache.streampark.e2e.cases.FlinkSQL200OnK8sTest + - name: FlinkSQL210-K8s + class: org.apache.streampark.e2e.cases.FlinkSQL210OnK8sTest + - name: FlinkSQL220-K8s + class: org.apache.streampark.e2e.cases.FlinkSQL220OnK8sTest + - name: FlinkSQL230-K8s + class: org.apache.streampark.e2e.cases.FlinkSQL230OnK8sTest steps: - name: Free Disk Space - # refer: https://github.com/jlumbroso/free-disk-space + if: contains(matrix.case.name, 'Flink') uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 with: dotnet: true @@ -213,19 +278,12 @@ jobs: return 1 } pull_with_retry testcontainers/ryuk:0.7.0 - pull_with_retry selenium/standalone-chrome:4.13.0 || true - - name: Run Test + pull_with_retry rancher/k3s:v1.30.5-k3s1 || true + - name: Run API Test run: | ./mvnw -B -f streampark-e2e/pom.xml -am \ -DfailIfNoTests=false \ -Dtest=${{ matrix.case.class }} test - - uses: actions/upload-artifact@v4 - if: always() - name: Upload Recording - with: - name: recording-${{ matrix.case.name }} - path: ${{ env.RECORDING_PATH }} - retention-days: 1 - uses: actions/upload-artifact@v4 if: always() name: Upload Projects Build Logs @@ -237,7 +295,7 @@ jobs: name: E2E - Result runs-on: ubuntu-latest timeout-minutes: 30 - needs: [ paths-filter, license-header, code-style, build, e2e ] + needs: [ paths-filter, license-header, code-style, build, api-e2e ] if: always() steps: - name: Status @@ -246,7 +304,7 @@ jobs: echo "Skip E2E!" exit 0 fi - if [[ ${{ needs.e2e.result }} != 'success' ]]; then + if [[ ${{ needs.api-e2e.result }} != 'success' ]]; then echo "E2E Failed!" exit 1 fi diff --git a/streampark-e2e/pom.xml b/streampark-e2e/pom.xml index a41604694d..f7bf17fccd 100644 --- a/streampark-e2e/pom.xml +++ b/streampark-e2e/pom.xml @@ -41,6 +41,7 @@ 1.7.36 2.17.2 31.0.1-jre + 2.13.5 @@ -127,6 +128,12 @@ ${lombok.version} provided + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + diff --git a/streampark-e2e/scripts/generate-flink-e2e.sh b/streampark-e2e/scripts/generate-flink-e2e.sh new file mode 100755 index 0000000000..98bfe38cf2 --- /dev/null +++ b/streampark-e2e/scripts/generate-flink-e2e.sh @@ -0,0 +1,484 @@ +#!/usr/bin/env bash +# Generates Flink version-specific E2E tests and docker resources. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +CASE_DIR="$ROOT/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases" +DOCKER_DIR="$ROOT/streampark-e2e-case/src/test/resources/docker" + +APACHE_LICENSE='/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */' + +YAML_LICENSE='# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +#' + +generate_remote_compose() { + local dir="$1" patch="$2" + cat >"$dir/docker-compose.yaml" <"$dir/Dockerfile" <"$dir/docker-compose.yaml" +} + +generate_remote_test() { + local class_prefix="$1" docker_key="$2" patch="$3" + cat >"$CASE_DIR/${class_prefix}OnRemoteClusterDeployTest.java" <addCluster(deployMode) + .jobManagerURL(flinkJobManagerUrl) + .clusterName(flinkClusterName) + .flinkVersion(flinkName) + .submit(); + + Awaitility.await() + .untilAsserted( + () -> assertThat(flinkClustersPage.flinkClusterList) + .as("Flink clusters list should contain newly-created application") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains(flinkClusterName))); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); + + flinkClustersPage.deleteFlinkCluster(flinkClusterName); + + Awaitility.await() + .untilAsserted( + () -> { + browser.navigate().refresh(); + Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); + assertThat(flinkClustersPage.flinkClusterList) + .noneMatch(it -> it.getText().contains(flinkClusterName)); + }); + } +} +EOF +} + +generate_yarn_cluster_test() { + local class_prefix="$1" docker_key="$2" patch="$3" + cat >"$CASE_DIR/${class_prefix}OnYarnClusterDeployTest.java" <addCluster(deployMode) + .resolveOrder(YarnSessionForm.ResolveOrder.CHILD_FIRST) + .clusterName(flinkClusterName) + .flinkVersion(flinkName) + .submit(); + + Awaitility.await() + .untilAsserted( + () -> assertThat(flinkClustersPage.flinkClusterList) + .as("Flink clusters list should contain newly-created application") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains(flinkClusterName))); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); + + flinkClustersPage.editFlinkCluster(flinkClusterName) + .addCluster(deployMode) + .clusterName(flinkClusterNameEdited) + .submit(); + + Awaitility.await() + .untilAsserted( + () -> assertThat(flinkClustersPage.flinkClusterList) + .as("Flink clusters list should contain edited application") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains(flinkClusterNameEdited))); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); + + flinkClustersPage.startFlinkCluster(flinkClusterNameEdited); + + Awaitility.await() + .untilAsserted( + () -> assertThat(flinkClustersPage.flinkClusterList) + .as("Flink clusters list should contain running application") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains("RUNNING"))); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); + + flinkClustersPage.stopFlinkCluster(flinkClusterNameEdited); + + Awaitility.await() + .untilAsserted( + () -> assertThat(flinkClustersPage.flinkClusterList) + .as("Flink clusters list should contain canceled application") + .extracting(WebElement::getText) + .anyMatch(it -> it.contains("CANCELED"))); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); + + flinkClustersPage.deleteFlinkCluster(flinkClusterNameEdited); + + Awaitility.await() + .untilAsserted( + () -> { + browser.navigate().refresh(); + Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); + assertThat(flinkClustersPage.flinkClusterList) + .noneMatch(it -> it.getText().contains(flinkClusterNameEdited)); + }); + } +} +EOF +} + +generate_sql_test() { + local class_prefix="$1" docker_key="$2" patch="$3" app_suffix="$4" + sed \ + -e "s/FlinkSQL120OnYarnTest/FlinkSQL${class_prefix#Flink}OnYarnTest/g" \ + -e "s/flink-1.20-on-yarn/flink-${docker_key}-on-yarn/g" \ + -e "s/flink-1.20.1/flink-${patch}/g" \ + -e "s/flink-120-e2e-test/flink-${app_suffix}-e2e-test/g" \ + -e "s/flink_1.20.1_cluster_e2e/flink_${patch}_cluster_e2e/g" \ + "$CASE_DIR/FlinkSQL120OnYarnTest.java" >"$CASE_DIR/FlinkSQL${class_prefix#Flink}OnYarnTest.java" +} + +generate_k8s_assets() { + local dir="$1" patch="$2" java_tag="$3" + local image="apache/streampark-flink-${patch}-on-k8s:ci" + cat >"$dir/Dockerfile" <"$dir/docker-compose.yaml" +} + +generate_k8s_cluster_test() { + local class_prefix="$1" docker_key="$2" patch="$3" java_tag="$4" + sed \ + -e "s/Flink120OnK8sClusterDeployTest/${class_prefix}OnK8sClusterDeployTest/g" \ + -e "s/flink-1.20-on-k8s/flink-${docker_key}-on-k8s/g" \ + -e "s/flink-1.20.1/flink-${patch}/g" \ + -e "s/flink_1.20.1_k8s_cluster_e2e/flink_${patch}_k8s_cluster_e2e/g" \ + -e "s/flink:1.20.1-scala_2.12-java8/flink:${patch}-scala_2.12-${java_tag}/g" \ + "$CASE_DIR/Flink120OnK8sClusterDeployTest.java" >"$CASE_DIR/${class_prefix}OnK8sClusterDeployTest.java" +} + +generate_k8s_sql_test() { + local class_prefix="$1" docker_key="$2" patch="$3" java_tag="$4" app_suffix="$5" + sed \ + -e "s/FlinkSQL120OnK8sTest/FlinkSQL${class_prefix#Flink}OnK8sTest/g" \ + -e "s/flink-1.20-on-k8s/flink-${docker_key}-on-k8s/g" \ + -e "s/flink-1.20.1/flink-${patch}/g" \ + -e "s/flink_1.20.1_k8s_cluster_e2e/flink_${patch}_k8s_cluster_e2e/g" \ + -e "s/flink120e2etest/flink${app_suffix}e2etest/g" \ + -e "s/flink120session/flink${app_suffix}session/g" \ + -e "s/flink:1.20.1-scala_2.12-java8/flink:${patch}-scala_2.12-${java_tag}/g" \ + "$CASE_DIR/FlinkSQL120OnK8sTest.java" >"$CASE_DIR/FlinkSQL${class_prefix#Flink}OnK8sTest.java" +} + +# docker_key|patch|class_prefix|java_tag|app_suffix +VERSIONS=( + "1.19|1.19.0|Flink119|java8|119" + "2.0|2.0.2|Flink200|java11|200" + "2.1|2.1.2|Flink210|java11|210" + "2.2|2.2.1|Flink220|java11|220" + "2.3|2.3.0|Flink230|java11|230" +) + +for entry in "${VERSIONS[@]}"; do + IFS='|' read -r docker_key patch class_prefix java_tag app_suffix <<<"$entry" + remote_dir="$DOCKER_DIR/flink-${docker_key}-on-remote" + yarn_dir="$DOCKER_DIR/flink-${docker_key}-on-yarn" + k8s_dir="$DOCKER_DIR/flink-${docker_key}-on-k8s" + mkdir -p "$remote_dir" "$yarn_dir" "$k8s_dir" + generate_remote_compose "$remote_dir" "$patch" + generate_yarn_assets "$yarn_dir" "$patch" "$java_tag" + generate_k8s_assets "$k8s_dir" "$patch" "$java_tag" + generate_remote_test "$class_prefix" "$docker_key" "$patch" + generate_yarn_cluster_test "$class_prefix" "$docker_key" "$patch" + generate_k8s_cluster_test "$class_prefix" "$docker_key" "$patch" "$java_tag" + generate_sql_test "$class_prefix" "$docker_key" "$patch" "$app_suffix" + generate_k8s_sql_test "$class_prefix" "$docker_key" "$patch" "$java_tag" "$app_suffix" + echo "Generated Flink ${patch} (${class_prefix})" +done + +# Generate K8s assets for baseline versions managed outside VERSIONS loop +BASELINE_VERSIONS=( + "1.17|1.17.2|Flink117|java8|117" + "1.18|1.18.1|Flink118|java8|118" +) +for entry in "${BASELINE_VERSIONS[@]}"; do + IFS='|' read -r docker_key patch class_prefix java_tag app_suffix <<<"$entry" + k8s_dir="$DOCKER_DIR/flink-${docker_key}-on-k8s" + mkdir -p "$k8s_dir" + generate_k8s_assets "$k8s_dir" "$patch" "$java_tag" + generate_k8s_cluster_test "$class_prefix" "$docker_key" "$patch" "$java_tag" + generate_k8s_sql_test "$class_prefix" "$docker_key" "$patch" "$java_tag" "$app_suffix" + echo "Generated K8s baseline Flink ${patch} (${class_prefix})" +done + +# Remove Flink 1.16 assets +rm -rf "$DOCKER_DIR/flink-1.16-on-remote" "$DOCKER_DIR/flink-1.16-on-yarn" +rm -f "$CASE_DIR"/Flink116*.java "$CASE_DIR"/FlinkSQL116*.java +echo "Removed Flink 1.16 E2E assets" diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/AlarmTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/AlarmTest.java index 87f1f4b17a..d3803433aa 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/AlarmTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/AlarmTest.java @@ -17,133 +17,110 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.setting.SettingPage; -import org.apache.streampark.e2e.pages.setting.alarm.AlarmPage; -import org.apache.streampark.e2e.pages.setting.alarm.AlertTypeDetailForm; -import org.apache.streampark.e2e.pages.setting.alarm.DingTalkAlertForm; -import org.apache.streampark.e2e.pages.setting.alarm.EmailAlertForm; -import org.apache.streampark.e2e.pages.setting.alarm.LarkAlertForm; -import org.apache.streampark.e2e.pages.setting.alarm.WeChatAlertForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class AlarmTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String newEmail = "new@streampark.com"; - private static final String newAlarmName = "new_alarm"; - private static final String editAlarmName = "edit_alarm"; + private static Long alertId; + @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SettingPage.class) - .goToTab(AlarmPage.class); + api.login(); } @Test @Order(1) public void testCreateAlarm() { - final AlarmPage alarmPage = new AlarmPage(browser); - - final String dingTalkURL = ""; - final String dingTalkToken = "dingTalkToken"; - final String dingTalkSecretToken = "dingTalkSecretToken"; - final String dingTalkReceiveUser = "dingTalkUser"; - - final String wechatToken = "wechatToken"; - - final String larkToken = "larkToken"; - final String larkSecretToken = "larkSecretToken"; - - AlertTypeDetailForm alertTypeDetailForm = alarmPage.createAlarm(); - alertTypeDetailForm - .addAlertType(AlertTypeDetailForm.AlertTypeEnum.EMAIL) - .email(newEmail) - .alertName(newAlarmName); - alertTypeDetailForm - .addAlertType(AlertTypeDetailForm.AlertTypeEnum.DINGTALK) - .url(dingTalkURL) - .token(dingTalkToken) - .secretEnable() - .secretToken(dingTalkSecretToken) - .effectToAllUsers() - .receiveUser(dingTalkReceiveUser); - alertTypeDetailForm - .addAlertType(AlertTypeDetailForm.AlertTypeEnum.WECHAT) - .token(wechatToken); - alertTypeDetailForm - .addAlertType(AlertTypeDetailForm.AlertTypeEnum.LARK) - .token(larkToken) - .secretEnable() - .secretToken(larkSecretToken) - .effectToAllUsers() - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(alarmPage.alarmList) - .as("Alarm list should contain newly-created alarm") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(newAlarmName)) - .anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.EMAIL.desc)) - .anyMatch(it -> it.contains(newEmail)) - .anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.DINGTALK.desc)) - .anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.WECHAT.desc)) - .anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.LARK.desc)) - .anyMatch(it -> it.contains(dingTalkReceiveUser))); + ApiResponse response = api.postJson("/flink/alert/add", api.writeJson(buildAlertBody(newAlarmName, 23, true))); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postJson("/flink/alert/list", "{}"); + assertThat(list.isSuccess()).isTrue(); + assertThat(list.getData().toString()).contains(newAlarmName); + assertThat(list.getData().toString()).contains(newEmail); } @Test @Order(2) public void testEditAlarm() { - final AlarmPage alarmPage = new AlarmPage(browser); - - alarmPage.editAlarm(newAlarmName) - // this step will cancel E-mail type click status. - .addAlertType(AlertTypeDetailForm.AlertTypeEnum.EMAIL) - .alertName(editAlarmName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(alarmPage.alarmList) - .as("Alarm list should contain edited alarm") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(editAlarmName)) - .noneMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.EMAIL.desc)) - .anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.DINGTALK.desc)) - .anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.WECHAT.desc)) - .anyMatch(it -> it.contains(AlertTypeDetailForm.AlertTypeEnum.LARK.desc)) - .noneMatch(it -> it.contains(newEmail))); + alertId = findAlertId(newAlarmName).orElseThrow(); + + ApiResponse response = + api.postJson("/flink/alert/update", api.writeJson(buildAlertBody(editAlarmName, 22, false))); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postJson("/flink/alert/list", "{}"); + assertThat(list.getData().toString()).contains(editAlarmName); + assertThat(list.getData().toString()).doesNotContain(newEmail); } @Test @Order(3) public void testDeleteAlarm() { - final AlarmPage alarmPage = new AlarmPage(browser); + ApiResponse response = api.deleteForm("/flink/alert/delete", api.params("id", String.valueOf(alertId))); + assertThat(response.isSuccess()).isTrue(); - alarmPage.deleteAlarm(editAlarmName); + ApiResponse list = api.postJson("/flink/alert/list", "{}"); + assertThat(list.getData().toString()).doesNotContain(editAlarmName); + } + + private static Optional findAlertId(String alertName) { + ApiResponse list = api.postJson("/flink/alert/list", "{}"); + if (list.getData() == null || !list.getData().isArray()) { + return Optional.empty(); + } + for (JsonNode item : list.getData()) { + if (alertName.equals(item.path("alertName").asText())) { + return Optional.of(item.path("id").asLong()); + } + } + return Optional.empty(); + } - Awaitility.await() - .untilAsserted( - () -> assertThat(alarmPage.alarmList) - .as(String.format("Alarm list shouldn't contain alarm witch named %s", editAlarmName)) - .extracting(WebElement::getText) - .noneMatch(it -> it.contains(editAlarmName))); + private static ObjectNode buildAlertBody(String alertName, int alertType, boolean withEmail) { + ObjectNode body = api.objectNode(); + if (alertId != null) { + body.put("id", alertId); + } + body.put("alertName", alertName); + body.put("alertType", alertType); + if (withEmail) { + ObjectNode emailParams = body.putObject("emailParams"); + emailParams.putArray("contacts").add(newEmail); + } + ObjectNode dingTalkParams = body.putObject("dingTalkParams"); + dingTalkParams.put("alertDingURL", ""); + dingTalkParams.put("token", "dingTalkToken"); + dingTalkParams.put("secretEnable", true); + dingTalkParams.put("secretToken", "dingTalkSecretToken"); + dingTalkParams.put("isAtAll", true); + dingTalkParams.putArray("contacts").add("dingTalkUser"); + ObjectNode weComParams = body.putObject("weComParams"); + weComParams.put("token", "wechatToken"); + ObjectNode larkParams = body.putObject("larkParams"); + larkParams.put("token", "larkToken"); + larkParams.put("secretEnable", true); + larkParams.put("secretToken", "larkSecretToken"); + larkParams.put("isAtAll", true); + return body; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/EnvironmentTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/EnvironmentTest.java index bf1988167b..f9d0b1c065 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/EnvironmentTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/EnvironmentTest.java @@ -17,175 +17,118 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.setting.SettingPage; -import org.apache.streampark.e2e.pages.setting.env.DockerSettingForm; -import org.apache.streampark.e2e.pages.setting.env.EmailSettingForm; -import org.apache.streampark.e2e.pages.setting.env.EnvironmentDetailForm; -import org.apache.streampark.e2e.pages.setting.env.EnvironmentPage; -import org.apache.streampark.e2e.pages.setting.env.IngressSettingForm; -import org.apache.streampark.e2e.pages.setting.env.MavenSettingForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.support.ui.WebDriverWait; -import org.testcontainers.shaded.org.awaitility.Awaitility; -import static org.apache.streampark.e2e.pages.common.CommonFactory.WebElementClick; +import java.util.LinkedHashMap; +import java.util.Map; + import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/environment/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/environment/docker-compose.yaml") public class EnvironmentTest { - public static RemoteWebDriver browser; - - // maven - final String mavenFilePath = "/maven/file/path"; - final String mavenCentralRepository = "https://mvnrepository.com/"; - final String mavenAuthUser = "maven_user"; - final String mavenAuthPassword = "maven_password"; - - // ingress - final String ingressDomainAddress = "https://localhost"; - - // docker - final String dockerAddress = "https://hub.docker.com/v2/"; - final String dockerNamespace = "hello"; - final String dockerUser = "docker_user"; - final String dockerPassword = "docker_password"; - - // email - final String emailHost = "smtp.163.com"; - final String editEmailHost = "postfix"; - final String emailPort = "25"; - final String emailAddress = "hello@163.com"; - final String editEmailAddress = "hello@postfix.com"; - final String emailUser = "email_password"; - final String emailPassword = "email_password"; + public static ApiClient api; + + private static final String mavenFilePath = "/maven/file/path"; + private static final String mavenCentralRepository = "https://mvnrepository.com/"; + private static final String mavenAuthUser = "maven_user"; + private static final String mavenAuthPassword = "maven_password"; + private static final String ingressDomainAddress = "https://localhost"; + private static final String dockerAddress = "https://hub.docker.com/v2/"; + private static final String dockerNamespace = "hello"; + private static final String dockerUser = "docker_user"; + private static final String dockerPassword = "docker_password"; + private static final String emailHost = "smtp.163.com"; + private static final String editEmailHost = "postfix"; + private static final String emailPort = "25"; + private static final String emailAddress = "hello@163.com"; + private static final String editEmailAddress = "hello@postfix.com"; + private static final String emailUser = "email_password"; + private static final String emailPassword = "email_password"; @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SettingPage.class) - .goToTab(EnvironmentPage.class); + api.login(); } @Test @Order(1) public void testCreateEnvironment() { - final EnvironmentPage environmentPage = new EnvironmentPage(browser); - - environmentPage.createEnvironment(EnvironmentDetailForm.EnvSettingTypeEnum.Maven) - .addSetting(EnvironmentDetailForm.EnvSettingTypeEnum.Maven) - .filePath(mavenFilePath) - .centralRepository(mavenCentralRepository) - .authUser(mavenAuthUser) - .authPassword(mavenAuthPassword); - - environmentPage.createEnvironment(EnvironmentDetailForm.EnvSettingTypeEnum.Ingress) - .addSetting(EnvironmentDetailForm.EnvSettingTypeEnum.Ingress) - .domainAddress(ingressDomainAddress); - - Awaitility.await() - .untilAsserted( - () -> assertThat(environmentPage.settingList) - .as("Setting list should contain newly-created setting") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(mavenFilePath)) - .anyMatch(it -> it.contains(mavenCentralRepository)) - .anyMatch(it -> it.contains(mavenAuthUser)) - .anyMatch(it -> it.contains(ingressDomainAddress))); + updateSetting("streampark.maven.settings", mavenFilePath); + updateSetting("streampark.maven.central.repository", mavenCentralRepository); + updateSetting("streampark.maven.auth.user", mavenAuthUser); + updateSetting("streampark.maven.auth.password", mavenAuthPassword); + updateSetting("ingress.mode.default", ingressDomainAddress); + + ApiResponse list = api.postForm("/setting/all", new LinkedHashMap<>()); + assertThat(list.isSuccess()).isTrue(); + String settings = list.getData().toString(); + assertThat(settings).contains(mavenFilePath); + assertThat(settings).contains(mavenCentralRepository); + assertThat(settings).contains(mavenAuthUser); + assertThat(settings).contains(ingressDomainAddress); } @Test @Order(2) public void testCreateEmailSettingFailedWithAuth() { - final EnvironmentPage environmentPage = new EnvironmentPage(browser); - - EmailSettingForm emailSettingForm = - environmentPage.createEnvironment(EnvironmentDetailForm.EnvSettingTypeEnum.Email) - .addSetting(EnvironmentDetailForm.EnvSettingTypeEnum.Email) - .host(emailHost) - .port(emailPort) - .address(emailAddress) - .user(emailUser) - .password(emailPassword) - .ok(); - - String expectedErrorMessage = - "connect to target mail server failed: 535 Error: authentication failed"; - Awaitility.await() - - .untilAsserted( - () -> { - new WebDriverWait(browser, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION); - assertThat(environmentPage.errorMessageList) - .as("Connect failed error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(expectedErrorMessage)); - }); - - WebElementClick(browser, environmentPage.errorMessageConfirmButton); - emailSettingForm.cancel(); + ApiResponse response = api.postForm("/setting/check/email", emailParams(emailHost, emailAddress)); + assertThat(response.isSuccess()).isTrue(); + assertThat(response.getData().path("msg").asText()) + .contains("connect to target mail server failed: 535 Error: authentication failed"); } @Test @Order(3) public void testCreateEmailSettingSuccessful() { - final EnvironmentPage environmentPage = new EnvironmentPage(browser); - - EmailSettingForm emailSettingForm = - environmentPage.createEnvironment(EnvironmentDetailForm.EnvSettingTypeEnum.Email) - .addSetting(EnvironmentDetailForm.EnvSettingTypeEnum.Email) - .host(editEmailHost) - .port(emailPort) - .address(editEmailAddress) - .user(emailUser) - .password(emailPassword) - .ok(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(environmentPage.settingList) - .as("Setting list should contain newly-created email setting") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(editEmailAddress))); + ApiResponse check = api.postForm("/setting/check/email", emailParams(editEmailHost, editEmailAddress)); + assertThat(check.isSuccess()).isTrue(); + + ApiResponse update = api.postForm("/setting/update/email", emailParams(editEmailHost, editEmailAddress)); + assertThat(update.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/setting/all", new LinkedHashMap<>()); + assertThat(list.getData().toString()).contains(editEmailAddress); } @Test @Order(4) public void testCreateDockerSettingFailed() { - final EnvironmentPage environmentPage = new EnvironmentPage(browser); - DockerSettingForm dockerSettingForm = - environmentPage.createEnvironment(EnvironmentDetailForm.EnvSettingTypeEnum.Docker) - .addSetting(EnvironmentDetailForm.EnvSettingTypeEnum.Docker) - .address(dockerAddress) - .namespace(dockerNamespace) - .user(dockerUser) - .password(dockerPassword) - .ok(); - - String expectedErrorMessage = String.format( - "Failed to validate Docker registry, error: Status 500: {\"message\":\"login attempt to %s failed with status: 404 Not Found\"}", - dockerAddress); - Awaitility.await() - - .untilAsserted( - () -> { - new WebDriverWait(browser, Constants.DEFAULT_WEBDRIVER_WAIT_DURATION); - assertThat(environmentPage.errorMessageList) - .as("Failed to validate docker registry error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(expectedErrorMessage)); - }); - - WebElementClick(browser, environmentPage.errorMessageConfirmButton); - dockerSettingForm.cancel(); + Map params = new LinkedHashMap<>(); + params.put("address", dockerAddress); + params.put("namespace", dockerNamespace); + params.put("username", dockerUser); + params.put("password", dockerPassword); + + ApiResponse response = api.postForm("/setting/check/docker", params); + assertThat(response.isSuccess()).isTrue(); + assertThat(response.getData().path("msg").asText()) + .contains( + String.format( + "Failed to validate Docker registry, error: Status 500: {\"message\":\"login attempt to %s failed with status: 404 Not Found\"}", + dockerAddress)); + } + + private static void updateSetting(String key, String value) { + ApiResponse response = + api.postForm("/setting/update", api.params("settingKey", key, "settingValue", value)); + assertThat(response.isSuccess()).isTrue(); + } + + private static Map emailParams(String host, String from) { + Map params = new LinkedHashMap<>(); + params.put("host", host); + params.put("port", emailPort); + params.put("from", from); + params.put("userName", emailUser); + params.put("password", emailPassword); + params.put("ssl", "false"); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ExternalLinkTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ExternalLinkTest.java index aba14b87be..6638de0854 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ExternalLinkTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ExternalLinkTest.java @@ -17,107 +17,117 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.setting.ExternalLinkPage; -import org.apache.streampark.e2e.pages.setting.SettingPage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class ExternalLinkTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String newLabel = "new_label"; - private static final String editLabel = "edit_label"; - private static final String newName = "new_name"; - private static final String color = "#b54f4f"; - private static final String newLink = "https://grafana/flink-monitoring?var-JobId=var-JobId=1"; @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SettingPage.class) - .goToTab(ExternalLinkPage.class); + api.login(); } @Test @Order(1) void testCreateExternalLink() { - final ExternalLinkPage externalLinkPage = new ExternalLinkPage(browser); - externalLinkPage.createExternalLink(newLabel, newName, color, newLink); - - Awaitility.await() - .untilAsserted( - () -> assertThat(externalLinkPage.externalLinkList) - .as("External link list should contain newly-created link") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(newLabel)) - .anyMatch(it -> it.contains(newName)) - .anyMatch(it -> it.contains(newLink))); + ApiResponse response = api.postForm("/flink/externalLink/create", linkParams(newLabel, newName, newLink)); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/flink/externalLink/list", new LinkedHashMap<>()); + assertThat(listContainsLink(list, newLabel, newName, newLink)).isTrue(); } @Test @Order(2) void testCreateDuplicateExternalLink() { - final ExternalLinkPage externalLinkPage = new ExternalLinkPage(browser); - externalLinkPage.createExternalLink(newLabel, newName, color, newLink); - - Awaitility.await() - .untilAsserted( - () -> assertThat(externalLinkPage.errorMessageList) - .as("Name Duplicated Error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains( - String.format("The name: %s is already existing.", newName)))); - - externalLinkPage.errorMessageConfirmButton.click(); - externalLinkPage.createExternalLinkForm.buttonCancel.click(); + ApiResponse response = api.postForm("/flink/externalLink/create", linkParams(newLabel, newName, newLink)); + assertThat(response.isSuccess()).isFalse(); + assertThat(response.getMessage()).contains(String.format("The name: %s is already existing.", newName)); } @Test @Order(3) void testEditExternalLink() { - final ExternalLinkPage externalLinkPage = new ExternalLinkPage(browser); String editName = "edit_name"; String editLink = "https://grafana/flink-monitoring?var-Job=var-Job=edit"; - externalLinkPage.editExternalLink(newLabel, editLabel, editName, color, editLink); - - Awaitility.await() - .untilAsserted( - () -> assertThat(externalLinkPage.externalLinkList) - .as("External link list should contain edited link") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(editLabel)) - .anyMatch(it -> it.contains(editName)) - .anyMatch(it -> it.contains(editLink))); + Long id = findLinkId(newLabel).orElseThrow(); + + Map params = linkParams(editLabel, editName, editLink); + params.put("id", String.valueOf(id)); + + ApiResponse response = api.postForm("/flink/externalLink/update", params); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/flink/externalLink/list", new LinkedHashMap<>()); + assertThat(listContainsLink(list, editLabel, editName, editLink)).isTrue(); } @Test @Order(4) void testDeleteExternalLink() { - final ExternalLinkPage externalLinkPage = new ExternalLinkPage(browser); + Long id = findLinkId(editLabel).orElseThrow(); + ApiResponse response = api.deleteForm("/flink/externalLink/delete", api.params("id", String.valueOf(id))); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/flink/externalLink/list", new LinkedHashMap<>()); + assertThat(findLinkId(editLabel)).isEmpty(); + assertThat(list.getData().toString()).doesNotContain(editLabel); + } - externalLinkPage.deleteExternalLink(editLabel); + private static Map linkParams(String label, String name, String linkUrl) { + Map params = new LinkedHashMap<>(); + params.put("badgeLabel", label); + params.put("badgeName", name); + params.put("badgeColor", color); + params.put("linkUrl", linkUrl); + return params; + } + + private static Optional findLinkId(String label) { + ApiResponse list = api.postForm("/flink/externalLink/list", new LinkedHashMap<>()); + if (list.getData() == null || !list.getData().isArray()) { + return Optional.empty(); + } + for (JsonNode item : list.getData()) { + if (label.equals(item.path("badgeLabel").asText())) { + return Optional.of(item.path("id").asLong()); + } + } + return Optional.empty(); + } - Awaitility.await() - .untilAsserted( - () -> { - assertThat(externalLinkPage.externalLinkList) - .noneMatch(it -> it.getText().contains(editLabel)); - }); + private static boolean listContainsLink(ApiResponse list, String label, String name, String linkUrl) { + if (list.getData() == null || !list.getData().isArray()) { + return false; + } + for (JsonNode item : list.getData()) { + if (label.equals(item.path("badgeLabel").asText()) + && name.equals(item.path("badgeName").asText()) + && linkUrl.equals(item.path("linkUrl").asText())) { + return true; + } + } + return false; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink116OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink116OnRemoteClusterDeployTest.java deleted file mode 100644 index b8eb70bb0d..0000000000 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink116OnRemoteClusterDeployTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.streampark.e2e.cases; - -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.RemoteForm; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; - -import static org.assertj.core.api.Assertions.assertThat; - -@StreamPark(composeFiles = "docker/flink-1.16-on-remote/docker-compose.yaml") -public class Flink116OnRemoteClusterDeployTest { - - public static RemoteWebDriver browser; - - private static final String flinkName = "flink-1.16.3"; - - private static final String flinkHome = "/opt/flink/"; - - private static final String flinkDescription = "description test"; - - private static final String flinkClusterName = "flink_1.16.3_cluster_e2e"; - - private static final String flinkJobManagerUrl = "http://jobmanager:8081"; - - private static final ClusterDetailForm.DeployMode deployMode = ClusterDetailForm.DeployMode.STANDALONE; - - @BeforeAll - public static void setUp() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); - } - - @Test - @Order(1) - public void testCreateFlinkCluster() { - FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.createFlinkCluster() - .addCluster(deployMode) - .jobManagerURL(flinkJobManagerUrl) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterName))); - } - - @Test - @Order(2) - public void testDeleteFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.deleteFlinkCluster(flinkClusterName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - assertThat(flinkClustersPage.flinkClusterList) - .noneMatch(it -> it.getText().contains(flinkClusterName)); - }); - } -} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink116OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink116OnYarnClusterDeployTest.java deleted file mode 100644 index b48221907e..0000000000 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink116OnYarnClusterDeployTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.streampark.e2e.cases; - -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.YarnSessionForm; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; - -import static org.assertj.core.api.Assertions.assertThat; - -@StreamPark(composeFiles = "docker/flink-1.16-on-yarn/docker-compose.yaml") -public class Flink116OnYarnClusterDeployTest { - - public static RemoteWebDriver browser; - - private static final String flinkName = "flink-1.16.3"; - - private static final String flinkHome = "/flink-1.16.3"; - - private static final String flinkDescription = "description test"; - - private static final String flinkClusterName = "flink_1.16.3_cluster_e2e"; - - private static final String flinkClusterNameEdited = "flink_1.16.3_cluster_e2e_edited"; - - private static final ClusterDetailForm.DeployMode deployMode = ClusterDetailForm.DeployMode.YARN_SESSION; - - @BeforeAll - public static void setUp() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); - } - - @Test - @Order(1) - public void testCreateFlinkCluster() { - FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.createFlinkCluster() - .addCluster(deployMode) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterName))); - } - - @Test - @Order(2) - public void testEditFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.editFlinkCluster(flinkClusterName) - .addCluster(deployMode) - .clusterName(flinkClusterNameEdited) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain edited application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterNameEdited))); - } - - @Test - @Order(3) - public void testStartFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.startFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain running application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - } - - @Test - @Order(4) - public void testStopFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.stopFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); - } - - @Test - @Order(5) - public void testDeleteFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.deleteFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - assertThat(flinkClustersPage.flinkClusterList) - .noneMatch(it -> it.getText().contains(flinkClusterNameEdited)); - }); - } -} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnK8sClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnK8sClusterDeployTest.java new file mode 100644 index 0000000000..504e2fd355 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnK8sClusterDeployTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.17-on-k8s/docker-compose.yaml") +public class Flink117OnK8sClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.17.2"; + + private static final String flinkHome = "/flink-1.17.2"; + + private static final String clusterName = "flink_1.17.2_k8s_cluster_e2e"; + + private static final String clusterNameEdited = "flink_1.17.2_k8s_cluster_e2e_edited"; + + private static final String flinkImage = "flink:1.17.2-scala_2.12-java8"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("id", clusterId); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnRemoteClusterDeployTest.java index fdb0c5d019..1b863a44ad 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnRemoteClusterDeployTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnRemoteClusterDeployTest.java @@ -17,88 +17,60 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.RemoteForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.17-on-remote/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.17-on-remote/docker-compose.yaml") public class Flink117OnRemoteClusterDeployTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.17.2"; private static final String flinkHome = "/opt/flink/"; - private static final String flinkDescription = "description test"; + private static final String clusterName = "flink_1.17.2_cluster_e2e"; - private static final String flinkClusterName = "flink_1.17.2_cluster_e2e"; + private static final String jobManagerUrl = "http://jobmanager:8081"; - private static final String flinkJobManagerUrl = "http://jobmanager:8081"; + private static long versionId; - private static final ClusterDetailForm.DeployMode deployMode = ClusterDetailForm.DeployMode.STANDALONE; + private static long clusterId; @BeforeAll - public static void setUp() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); } @Test @Order(1) public void testCreateFlinkCluster() { - FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.createFlinkCluster() - .addCluster(deployMode) - .jobManagerURL(flinkJobManagerUrl) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterName))); + ObjectNode body = flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_STANDALONE, versionId); + body.put("address", jobManagerUrl); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); } @Test @Order(5) public void testDeleteFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.deleteFlinkCluster(flinkClusterName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - assertThat(flinkClustersPage.flinkClusterList) - .noneMatch(it -> it.getText().contains(flinkClusterName)); - }); + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterName)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnYarnClusterDeployTest.java index 34e145f9ea..eab4269ba6 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnYarnClusterDeployTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink117OnYarnClusterDeployTest.java @@ -17,136 +17,89 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.YarnSessionForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.time.Duration; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.17-on-yarn/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.17-on-yarn/docker-compose.yaml") public class Flink117OnYarnClusterDeployTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.17.2"; private static final String flinkHome = "/flink-1.17.2"; - private static final String flinkDescription = "description test"; + private static final String clusterName = "flink_1.17.2_cluster_e2e"; - private static final String flinkClusterName = "flink_1.17.2_cluster_e2e"; + private static final String clusterNameEdited = "flink_1.17.2_cluster_e2e_edited"; - private static final String flinkClusterNameEdited = "flink_1.17.2_cluster_e2e_edited"; + private static long versionId; - private static final ClusterDetailForm.DeployMode deployMode = ClusterDetailForm.DeployMode.YARN_SESSION; + private static long clusterId; @BeforeAll public static void setup() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); } @Test @Order(1) public void testCreateFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.createFlinkCluster() - .addCluster(deployMode) - .resolveOrder(YarnSessionForm.ResolveOrder.CHILD_FIRST) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterName))); + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); } @Test @Order(2) public void testEditFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.editFlinkCluster(flinkClusterName) - .addCluster(deployMode) - .clusterName(flinkClusterNameEdited) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain edited application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterNameEdited))); + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("id", clusterId); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); } @Test @Order(3) public void testStartFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.startFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain running application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); } @Test @Order(4) public void testStopFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.stopFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(15)); } @Test @Order(5) public void testDeleteFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.deleteFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - assertThat(flinkClustersPage.flinkClusterList) - .noneMatch(it -> it.getText().contains(flinkClusterNameEdited)); - }); + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnK8sClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnK8sClusterDeployTest.java new file mode 100644 index 0000000000..f9ef3fecca --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnK8sClusterDeployTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.18-on-k8s/docker-compose.yaml") +public class Flink118OnK8sClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.18.1"; + + private static final String flinkHome = "/flink-1.18.1"; + + private static final String clusterName = "flink_1.18.1_k8s_cluster_e2e"; + + private static final String clusterNameEdited = "flink_1.18.1_k8s_cluster_e2e_edited"; + + private static final String flinkImage = "flink:1.18.1-scala_2.12-java8"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("id", clusterId); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnRemoteClusterDeployTest.java index 60f07c97a3..30fcbabc6f 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnRemoteClusterDeployTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnRemoteClusterDeployTest.java @@ -17,88 +17,60 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.RemoteForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.18-on-remote/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.18-on-remote/docker-compose.yaml") public class Flink118OnRemoteClusterDeployTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.18.1"; private static final String flinkHome = "/opt/flink/"; - private static final String flinkDescription = "description test"; + private static final String clusterName = "flink_1.18.1_cluster_e2e"; - private static final String flinkClusterName = "flink_1.18.1_cluster_e2e"; + private static final String jobManagerUrl = "http://jobmanager:8081"; - private static final String flinkJobManagerUrl = "http://jobmanager:8081"; + private static long versionId; - private static final ClusterDetailForm.DeployMode deployMode = ClusterDetailForm.DeployMode.STANDALONE; + private static long clusterId; @BeforeAll - public static void setUp() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); } @Test @Order(1) public void testCreateFlinkCluster() { - FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.createFlinkCluster() - .addCluster(deployMode) - .jobManagerURL(flinkJobManagerUrl) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterName))); + ObjectNode body = flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_STANDALONE, versionId); + body.put("address", jobManagerUrl); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); } @Test @Order(5) public void testDeleteFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.deleteFlinkCluster(flinkClusterName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - assertThat(flinkClustersPage.flinkClusterList) - .noneMatch(it -> it.getText().contains(flinkClusterName)); - }); + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterName)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnYarnClusterDeployTest.java index cadb7a39d2..75fb1be346 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnYarnClusterDeployTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink118OnYarnClusterDeployTest.java @@ -17,136 +17,89 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.YarnSessionForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.time.Duration; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.18-on-yarn/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.18-on-yarn/docker-compose.yaml") public class Flink118OnYarnClusterDeployTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.18.1"; private static final String flinkHome = "/flink-1.18.1"; - private static final String flinkDescription = "description test"; + private static final String clusterName = "flink_1.18.1_cluster_e2e"; - private static final String flinkClusterName = "flink_1.18.1_cluster_e2e"; + private static final String clusterNameEdited = "flink_1.18.1_cluster_e2e_edited"; - private static final String flinkClusterNameEdited = "flink_1.18.1_cluster_e2e_edited"; + private static long versionId; - private static final ClusterDetailForm.DeployMode deployMode = ClusterDetailForm.DeployMode.YARN_SESSION; + private static long clusterId; @BeforeAll public static void setup() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); } @Test @Order(1) public void testCreateFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.createFlinkCluster() - .addCluster(deployMode) - .resolveOrder(YarnSessionForm.ResolveOrder.CHILD_FIRST) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterName))); + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); } @Test @Order(2) public void testEditFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.editFlinkCluster(flinkClusterName) - .addCluster(deployMode) - .clusterName(flinkClusterNameEdited) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain edited application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterNameEdited))); + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("id", clusterId); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); } @Test @Order(3) public void testStartFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.startFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain running application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); } @Test @Order(4) public void testStopFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.stopFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(15)); } @Test @Order(5) public void testDeleteFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.deleteFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - assertThat(flinkClustersPage.flinkClusterList) - .noneMatch(it -> it.getText().contains(flinkClusterNameEdited)); - }); + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnK8sClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnK8sClusterDeployTest.java new file mode 100644 index 0000000000..0e31ac667c --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnK8sClusterDeployTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.19-on-k8s/docker-compose.yaml") +public class Flink119OnK8sClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.19.0"; + + private static final String flinkHome = "/flink-1.19.0"; + + private static final String clusterName = "flink_1.19.0_k8s_cluster_e2e"; + + private static final String clusterNameEdited = "flink_1.19.0_k8s_cluster_e2e_edited"; + + private static final String flinkImage = "flink:1.19.0-scala_2.12-java8"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("id", clusterId); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnRemoteClusterDeployTest.java new file mode 100644 index 0000000000..3a4db6ba9e --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnRemoteClusterDeployTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.19-on-remote/docker-compose.yaml") +public class Flink119OnRemoteClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.19.0"; + + private static final String flinkHome = "/opt/flink/"; + + private static final String clusterName = "flink_1.19.0_cluster_e2e"; + + private static final String jobManagerUrl = "http://jobmanager:8081"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_STANDALONE, versionId); + body.put("address", jobManagerUrl); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnYarnClusterDeployTest.java new file mode 100644 index 0000000000..a3f44888f9 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink119OnYarnClusterDeployTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.19-on-yarn/docker-compose.yaml") +public class Flink119OnYarnClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.19.0"; + + private static final String flinkHome = "/flink-1.19.0"; + + private static final String clusterName = "flink_1.19.0_cluster_e2e"; + + private static final String clusterNameEdited = "flink_1.19.0_cluster_e2e_edited"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("id", clusterId); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(15)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnK8sClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnK8sClusterDeployTest.java new file mode 100644 index 0000000000..36b974fcf0 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnK8sClusterDeployTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.20-on-k8s/docker-compose.yaml") +public class Flink120OnK8sClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.20.1"; + + private static final String flinkHome = "/flink-1.20.1"; + + private static final String clusterName = "flink_1.20.1_k8s_cluster_e2e"; + + private static final String clusterNameEdited = "flink_1.20.1_k8s_cluster_e2e_edited"; + + private static final String flinkImage = "flink:1.20.1-scala_2.12-java8"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("id", clusterId); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnRemoteClusterDeployTest.java index 74c905a8ec..0b4771df20 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnRemoteClusterDeployTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnRemoteClusterDeployTest.java @@ -17,88 +17,60 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.RemoteForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.20-on-remote/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.20-on-remote/docker-compose.yaml") public class Flink120OnRemoteClusterDeployTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.20.1"; private static final String flinkHome = "/opt/flink/"; - private static final String flinkDescription = "description test"; + private static final String clusterName = "flink_1.20.1_cluster_e2e"; - private static final String flinkClusterName = "flink_1.20.1_cluster_e2e"; + private static final String jobManagerUrl = "http://jobmanager:8081"; - private static final String flinkJobManagerUrl = "http://jobmanager:8081"; + private static long versionId; - private static final ClusterDetailForm.DeployMode deployMode = ClusterDetailForm.DeployMode.STANDALONE; + private static long clusterId; @BeforeAll - public static void setUp() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); } @Test @Order(1) public void testCreateFlinkCluster() { - FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.createFlinkCluster() - .addCluster(deployMode) - .jobManagerURL(flinkJobManagerUrl) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterName))); + ObjectNode body = flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_STANDALONE, versionId); + body.put("address", jobManagerUrl); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); } @Test @Order(5) public void testDeleteFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.deleteFlinkCluster(flinkClusterName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - assertThat(flinkClustersPage.flinkClusterList) - .noneMatch(it -> it.getText().contains(flinkClusterName)); - }); + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterName)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnYarnClusterDeployTest.java index d6d379cc5d..564f6ce888 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnYarnClusterDeployTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink120OnYarnClusterDeployTest.java @@ -17,136 +17,89 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.YarnSessionForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.time.Duration; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.20-on-yarn/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.20-on-yarn/docker-compose.yaml") public class Flink120OnYarnClusterDeployTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.20.1"; private static final String flinkHome = "/flink-1.20.1"; - private static final String flinkDescription = "description test"; + private static final String clusterName = "flink_1.20.1_cluster_e2e"; - private static final String flinkClusterName = "flink_1.20.1_cluster_e2e"; + private static final String clusterNameEdited = "flink_1.20.1_cluster_e2e_edited"; - private static final String flinkClusterNameEdited = "flink_1.20.1_cluster_e2e_edited"; + private static long versionId; - private static final ClusterDetailForm.DeployMode deployMode = ClusterDetailForm.DeployMode.YARN_SESSION; + private static long clusterId; @BeforeAll public static void setup() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); } @Test @Order(1) public void testCreateFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.createFlinkCluster() - .addCluster(deployMode) - .resolveOrder(YarnSessionForm.ResolveOrder.CHILD_FIRST) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterName))); + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); } @Test @Order(2) public void testEditFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.editFlinkCluster(flinkClusterName) - .addCluster(deployMode) - .clusterName(flinkClusterNameEdited) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain edited application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(flinkClusterNameEdited))); + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("id", clusterId); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); } @Test @Order(3) public void testStartFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.startFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain running application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); } @Test @Order(4) public void testStopFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.stopFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> assertThat(flinkClustersPage.flinkClusterList) - .as("Flink clusters list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(15)); } @Test @Order(5) public void testDeleteFlinkCluster() { - final FlinkClustersPage flinkClustersPage = new FlinkClustersPage(browser); - - flinkClustersPage.deleteFlinkCluster(flinkClusterNameEdited); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - assertThat(flinkClustersPage.flinkClusterList) - .noneMatch(it -> it.getText().contains(flinkClusterNameEdited)); - }); + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnK8sClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnK8sClusterDeployTest.java new file mode 100644 index 0000000000..94e38decd1 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnK8sClusterDeployTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.0-on-k8s/docker-compose.yaml") +public class Flink200OnK8sClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.0.2"; + + private static final String flinkHome = "/flink-2.0.2"; + + private static final String clusterName = "flink_2.0.2_k8s_cluster_e2e"; + + private static final String clusterNameEdited = "flink_2.0.2_k8s_cluster_e2e_edited"; + + private static final String flinkImage = "flink:2.0.2-scala_2.12-java11"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("id", clusterId); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnRemoteClusterDeployTest.java new file mode 100644 index 0000000000..6b9a54f677 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnRemoteClusterDeployTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.0-on-remote/docker-compose.yaml") +public class Flink200OnRemoteClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.0.2"; + + private static final String flinkHome = "/opt/flink/"; + + private static final String clusterName = "flink_2.0.2_cluster_e2e"; + + private static final String jobManagerUrl = "http://jobmanager:8081"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_STANDALONE, versionId); + body.put("address", jobManagerUrl); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnYarnClusterDeployTest.java new file mode 100644 index 0000000000..d8c359926d --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink200OnYarnClusterDeployTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.0-on-yarn/docker-compose.yaml") +public class Flink200OnYarnClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.0.2"; + + private static final String flinkHome = "/flink-2.0.2"; + + private static final String clusterName = "flink_2.0.2_cluster_e2e"; + + private static final String clusterNameEdited = "flink_2.0.2_cluster_e2e_edited"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("id", clusterId); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(15)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnK8sClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnK8sClusterDeployTest.java new file mode 100644 index 0000000000..4ebc8e75aa --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnK8sClusterDeployTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.1-on-k8s/docker-compose.yaml") +public class Flink210OnK8sClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.1.2"; + + private static final String flinkHome = "/flink-2.1.2"; + + private static final String clusterName = "flink_2.1.2_k8s_cluster_e2e"; + + private static final String clusterNameEdited = "flink_2.1.2_k8s_cluster_e2e_edited"; + + private static final String flinkImage = "flink:2.1.2-scala_2.12-java11"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("id", clusterId); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnRemoteClusterDeployTest.java new file mode 100644 index 0000000000..8c8f7879a1 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnRemoteClusterDeployTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.1-on-remote/docker-compose.yaml") +public class Flink210OnRemoteClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.1.2"; + + private static final String flinkHome = "/opt/flink/"; + + private static final String clusterName = "flink_2.1.2_cluster_e2e"; + + private static final String jobManagerUrl = "http://jobmanager:8081"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_STANDALONE, versionId); + body.put("address", jobManagerUrl); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnYarnClusterDeployTest.java new file mode 100644 index 0000000000..2b09e45308 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink210OnYarnClusterDeployTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.1-on-yarn/docker-compose.yaml") +public class Flink210OnYarnClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.1.2"; + + private static final String flinkHome = "/flink-2.1.2"; + + private static final String clusterName = "flink_2.1.2_cluster_e2e"; + + private static final String clusterNameEdited = "flink_2.1.2_cluster_e2e_edited"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("id", clusterId); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(15)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnK8sClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnK8sClusterDeployTest.java new file mode 100644 index 0000000000..31dbbbfd18 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnK8sClusterDeployTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.2-on-k8s/docker-compose.yaml") +public class Flink220OnK8sClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.2.1"; + + private static final String flinkHome = "/flink-2.2.1"; + + private static final String clusterName = "flink_2.2.1_k8s_cluster_e2e"; + + private static final String clusterNameEdited = "flink_2.2.1_k8s_cluster_e2e_edited"; + + private static final String flinkImage = "flink:2.2.1-scala_2.12-java11"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("id", clusterId); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnRemoteClusterDeployTest.java new file mode 100644 index 0000000000..82a77a81dd --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnRemoteClusterDeployTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.2-on-remote/docker-compose.yaml") +public class Flink220OnRemoteClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.2.1"; + + private static final String flinkHome = "/opt/flink/"; + + private static final String clusterName = "flink_2.2.1_cluster_e2e"; + + private static final String jobManagerUrl = "http://jobmanager:8081"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_STANDALONE, versionId); + body.put("address", jobManagerUrl); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnYarnClusterDeployTest.java new file mode 100644 index 0000000000..73773002d3 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink220OnYarnClusterDeployTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.2-on-yarn/docker-compose.yaml") +public class Flink220OnYarnClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.2.1"; + + private static final String flinkHome = "/flink-2.2.1"; + + private static final String clusterName = "flink_2.2.1_cluster_e2e"; + + private static final String clusterNameEdited = "flink_2.2.1_cluster_e2e_edited"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("id", clusterId); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(15)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnK8sClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnK8sClusterDeployTest.java new file mode 100644 index 0000000000..8068718955 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnK8sClusterDeployTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.3-on-k8s/docker-compose.yaml") +public class Flink230OnK8sClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.3.0"; + + private static final String flinkHome = "/flink-2.3.0"; + + private static final String clusterName = "flink_2.3.0_k8s_cluster_e2e"; + + private static final String clusterNameEdited = "flink_2.3.0_k8s_cluster_e2e_edited"; + + private static final String flinkImage = "flink:2.3.0-scala_2.12-java11"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("id", clusterId); + body.put("flinkImage", flinkImage); + body.put("k8sNamespace", k8sNamespace); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnRemoteClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnRemoteClusterDeployTest.java new file mode 100644 index 0000000000..7e1f32043d --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnRemoteClusterDeployTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.3-on-remote/docker-compose.yaml") +public class Flink230OnRemoteClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.3.0"; + + private static final String flinkHome = "/opt/flink/"; + + private static final String clusterName = "flink_2.3.0_cluster_e2e"; + + private static final String jobManagerUrl = "http://jobmanager:8081"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_STANDALONE, versionId); + body.put("address", jobManagerUrl); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnYarnClusterDeployTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnYarnClusterDeployTest.java new file mode 100644 index 0000000000..54fa482373 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/Flink230OnYarnClusterDeployTest.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.3-on-yarn/docker-compose.yaml") +public class Flink230OnYarnClusterDeployTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.3.0"; + + private static final String flinkHome = "/flink-2.3.0"; + + private static final String clusterName = "flink_2.3.0_cluster_e2e"; + + private static final String clusterNameEdited = "flink_2.3.0_cluster_e2e_edited"; + + private static long versionId; + + private static long clusterId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + } + + @Test + @Order(1) + public void testCreateFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_CHILD_FIRST); + clusterId = flink.createCluster(body); + assertThat(clusterId).isPositive(); + } + + @Test + @Order(2) + public void testEditFlinkCluster() { + ObjectNode body = + flink.baseClusterBody(clusterNameEdited, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("id", clusterId); + flink.updateCluster(body); + assertThat(flink.requireClusterId(clusterNameEdited)).isEqualTo(clusterId); + } + + @Test + @Order(3) + public void testStartFlinkCluster() { + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(4) + public void testStopFlinkCluster() { + flink.shutdownCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_CANCELED, Duration.ofMinutes(15)); + } + + @Test + @Order(5) + public void testDeleteFlinkCluster() { + flink.deleteCluster(clusterId); + assertThatThrownBy(() -> flink.requireClusterId(clusterNameEdited)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL116OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL116OnYarnTest.java deleted file mode 100644 index a08141a9a9..0000000000 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL116OnYarnTest.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.streampark.e2e.cases; - -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.applications.ApplicationForm; -import org.apache.streampark.e2e.pages.flink.applications.ApplicationsPage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.YarnSessionForm; - -import lombok.SneakyThrows; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; - -import static org.apache.streampark.e2e.pages.common.Constants.TEST_FLINK_SQL; -import static org.assertj.core.api.Assertions.assertThat; - -@StreamPark(composeFiles = "docker/flink-1.16-on-yarn/docker-compose.yaml") -public class FlinkSQL116OnYarnTest { - - public static RemoteWebDriver browser; - - private static final String flinkName = "flink-1.16.3"; - - private static final String flinkHome = "/flink-1.16.3"; - - private static final String applicationName = "flink-116-e2e-test"; - - private static final String flinkDescription = "description test"; - - private static final String flinkClusterName = "flink_1.16.3_cluster_e2e"; - - @BeforeAll - public static void setup() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); - - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); - - FlinkClustersPage flinkClustersPage = flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); - - flinkClustersPage.createFlinkCluster() - .addCluster(ClusterDetailForm.DeployMode.YARN_SESSION) - .resolveOrder(YarnSessionForm.ResolveOrder.PARENT_FIRST) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); - - flinkClustersPage.startFlinkCluster(flinkClusterName); - - flinkClustersPage.goToNav(ApacheFlinkPage.class) - .goToTab(ApplicationsPage.class); - } - - @Test - @Order(1) - void testCreateFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_APPLICATION, - applicationName) - .flinkVersion(flinkName) - .flinkSql(TEST_FLINK_SQL) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); - } - - @Test - @Order(2) - void testReleaseFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); - } - - @Test - @Order(3) - void testStartFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); - } - - @Test - @Order(4) - @SneakyThrows - void testCancelFlinkApplicationOnYarnApplicationMode() { - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain restarted application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - applicationsPage.cancelApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); - } - - @Test - @Order(5) - void testDeleteFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); - } - - @Test - @Order(6) - void testCreateFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_PER_JOB, - applicationName) - .flinkVersion(flinkName) - .flinkSql(TEST_FLINK_SQL) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); - } - - @Test - @Order(7) - void testReleaseFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); - } - - @Test - @Order(8) - void testStartFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain started application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); - } - - @Test - @Order(9) - void testDeleteFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); - } - - @Test - @Order(10) - void testCreateFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_SESSION, - applicationName) - .flinkVersion(flinkName) - .flinkSql(TEST_FLINK_SQL) - .flinkCluster(flinkClusterName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); - } - - @Test - @Order(11) - void testReleaseFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); - } - - @Test - @Order(12) - void testStartFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain started application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); - } - - @Test - @Order(13) - void testDeleteFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); - } -} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL117OnK8sTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL117OnK8sTest.java new file mode 100644 index 0000000000..f3bb40bee7 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL117OnK8sTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.17-on-k8s/docker-compose.yaml") +public class FlinkSQL117OnK8sTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.17.2"; + + private static final String flinkHome = "/flink-1.17.2"; + + private static final String clusterName = "flink_1.17.2_k8s_cluster_e2e"; + + private static final String k8sAppJobName = "flink117e2etest"; + + private static final String k8sSessionJobName = "flink117session"; + + private static final String flinkImage = "flink:1.17.2-scala_2.12-java8"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + private static long k8sAppId; + + private static long k8sSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterBody.put("flinkImage", flinkImage); + clusterBody.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnKubernetesApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sAppJobName, FlinkApiConstants.DEPLOY_K8S_APPLICATION, versionId); + body.put("k8sNamespace", k8sNamespace); + body.put("flinkImage", flinkImage); + k8sAppId = flink.createApp(body); + assertThat(k8sAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnKubernetesApplicationMode() { + flink.releaseApp(k8sAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnKubernetesApplicationMode() { + flink.deleteApp(k8sAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnKubernetesSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sSessionJobName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("flinkClusterId", clusterId); + k8sSessionAppId = flink.createApp(body); + assertThat(k8sSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnKubernetesSessionMode() { + flink.releaseApp(k8sSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnKubernetesSessionMode() { + flink.deleteApp(k8sSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL117OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL117OnYarnTest.java index c2cdaff273..820b3b72b2 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL117OnYarnTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL117OnYarnTest.java @@ -17,341 +17,144 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.applications.ApplicationForm; -import org.apache.streampark.e2e.pages.flink.applications.ApplicationsPage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.YarnSessionForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; -import lombok.SneakyThrows; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.time.Duration; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.17-on-yarn/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.17-on-yarn/docker-compose.yaml") public class FlinkSQL117OnYarnTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.17.2"; private static final String flinkHome = "/flink-1.17.2"; - private static final String applicationName = "flink-117-e2e-test"; + private static final String clusterName = "flink_1.17.2_cluster_e2e"; - private static final String flinkDescription = "description test"; + private static final String yarnAppJobName = "flink-117-e2e-test"; - private static final String flinkClusterName = "flink_1.17.2_cluster_e2e"; - - @BeforeAll - public static void setup() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); + private static final String yarnSessionJobName = "flink-117-session-e2e"; - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); + private static long versionId; - FlinkClustersPage flinkClustersPage = flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + private static long clusterId; - flinkClustersPage.createFlinkCluster() - .addCluster(ClusterDetailForm.DeployMode.YARN_SESSION) - .resolveOrder(YarnSessionForm.ResolveOrder.PARENT_FIRST) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); + private static long yarnAppId; - flinkClustersPage.startFlinkCluster(flinkClusterName); + private static long yarnSessionAppId; - flinkClustersPage.goToNav(ApacheFlinkPage.class) - .goToTab(ApplicationsPage.class); + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); } @Test @Order(1) void testCreateFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_APPLICATION, - applicationName) - .flinkVersion(flinkName) - .flinkSql(Constants.TEST_FLINK_SQL) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); + ObjectNode body = + flink.baseSqlAppBody(yarnAppJobName, FlinkApiConstants.DEPLOY_YARN_APPLICATION, versionId); + yarnAppId = flink.createApp(body); + assertThat(yarnAppId).isPositive(); } @Test @Order(2) void testReleaseFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); + flink.releaseApp(yarnAppId); } @Test @Order(3) void testStartFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); } @Test @Order(4) - @SneakyThrows void testCancelFlinkApplicationOnYarnApplicationMode() { - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain restarted application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - applicationsPage.cancelApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); } @Test @Order(5) void testDeleteFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); + flink.deleteApp(yarnAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnAppJobName)) + .isInstanceOf(IllegalStateException.class); } @Test @Order(6) - void testCreateFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_PER_JOB, - applicationName) - .flinkVersion(flinkName) - .flinkSql(Constants.TEST_FLINK_SQL) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); + void testCreateFlinkApplicationOnYarnSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnSessionJobName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("flinkClusterId", clusterId); + yarnSessionAppId = flink.createApp(body); + assertThat(yarnSessionAppId).isPositive(); } @Test @Order(7) - void testReleaseFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); + void testReleaseFlinkApplicationOnYarnSessionMode() { + flink.releaseApp(yarnSessionAppId); } @Test @Order(8) - void testStartFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain started application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); + void testStartFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); } @Test @Order(9) - void testDeleteFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); + void testCancelFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); } @Test @Order(10) - void testCreateFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_SESSION, - applicationName) - .flinkVersion(flinkName) - .flinkSql(Constants.TEST_FLINK_SQL) - .flinkCluster(flinkClusterName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); - } - - @Test - @Order(11) - void testReleaseFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); - } - - @Test - @Order(12) - void testStartFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain started application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); - } - - @Test - @Order(13) - @SneakyThrows - void testRestartAndCancelFlinkApplicationOnYarnSessionMode() { - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain restarted application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - applicationsPage.cancelApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); - } - - @Test - @Order(14) void testDeleteFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); + flink.deleteApp(yarnSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnSessionJobName)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL118OnK8sTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL118OnK8sTest.java new file mode 100644 index 0000000000..4016193c9f --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL118OnK8sTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.18-on-k8s/docker-compose.yaml") +public class FlinkSQL118OnK8sTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.18.1"; + + private static final String flinkHome = "/flink-1.18.1"; + + private static final String clusterName = "flink_1.18.1_k8s_cluster_e2e"; + + private static final String k8sAppJobName = "flink118e2etest"; + + private static final String k8sSessionJobName = "flink118session"; + + private static final String flinkImage = "flink:1.18.1-scala_2.12-java8"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + private static long k8sAppId; + + private static long k8sSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterBody.put("flinkImage", flinkImage); + clusterBody.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnKubernetesApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sAppJobName, FlinkApiConstants.DEPLOY_K8S_APPLICATION, versionId); + body.put("k8sNamespace", k8sNamespace); + body.put("flinkImage", flinkImage); + k8sAppId = flink.createApp(body); + assertThat(k8sAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnKubernetesApplicationMode() { + flink.releaseApp(k8sAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnKubernetesApplicationMode() { + flink.deleteApp(k8sAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnKubernetesSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sSessionJobName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("flinkClusterId", clusterId); + k8sSessionAppId = flink.createApp(body); + assertThat(k8sSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnKubernetesSessionMode() { + flink.releaseApp(k8sSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnKubernetesSessionMode() { + flink.deleteApp(k8sSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL118OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL118OnYarnTest.java index df8e126cd9..4c52cb1836 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL118OnYarnTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL118OnYarnTest.java @@ -17,355 +17,144 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.applications.ApplicationForm; -import org.apache.streampark.e2e.pages.flink.applications.ApplicationsPage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.YarnSessionForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; -import lombok.SneakyThrows; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.time.Duration; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.18-on-yarn/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.18-on-yarn/docker-compose.yaml") public class FlinkSQL118OnYarnTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.18.1"; private static final String flinkHome = "/flink-1.18.1"; - private static final String applicationName = "flink-118-e2e-test"; + private static final String clusterName = "flink_1.18.1_cluster_e2e"; - private static final String flinkDescription = "description test"; + private static final String yarnAppJobName = "flink-118-e2e-test"; - private static final String flinkClusterName = "flink_1.18.1_cluster_e2e"; + private static final String yarnSessionJobName = "flink-118-session-e2e"; - @BeforeAll - public static void setup() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); + private static long versionId; - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); + private static long clusterId; - FlinkClustersPage flinkClustersPage = flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + private static long yarnAppId; - flinkClustersPage.createFlinkCluster() - .addCluster(ClusterDetailForm.DeployMode.YARN_SESSION) - .resolveOrder(YarnSessionForm.ResolveOrder.PARENT_FIRST) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); + private static long yarnSessionAppId; - flinkClustersPage.startFlinkCluster(flinkClusterName); + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); - flinkClustersPage.goToNav(ApacheFlinkPage.class) - .goToTab(ApplicationsPage.class); + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); } @Test @Order(1) void testCreateFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_APPLICATION, - applicationName) - .flinkVersion(flinkName) - .flinkSql(Constants.TEST_FLINK_SQL) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); + ObjectNode body = + flink.baseSqlAppBody(yarnAppJobName, FlinkApiConstants.DEPLOY_YARN_APPLICATION, versionId); + yarnAppId = flink.createApp(body); + assertThat(yarnAppId).isPositive(); } @Test @Order(2) void testReleaseFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); + flink.releaseApp(yarnAppId); } @Test @Order(3) void testStartFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); } @Test @Order(4) - @SneakyThrows void testCancelFlinkApplicationOnYarnApplicationMode() { - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain restarted application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - applicationsPage.cancelApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); } @Test @Order(5) void testDeleteFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); + flink.deleteApp(yarnAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnAppJobName)) + .isInstanceOf(IllegalStateException.class); } @Test @Order(6) - void testCreateFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_PER_JOB, - applicationName) - .flinkVersion(flinkName) - .flinkSql(Constants.TEST_FLINK_SQL) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); + void testCreateFlinkApplicationOnYarnSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnSessionJobName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("flinkClusterId", clusterId); + yarnSessionAppId = flink.createApp(body); + assertThat(yarnSessionAppId).isPositive(); } @Test @Order(7) - void testReleaseFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); + void testReleaseFlinkApplicationOnYarnSessionMode() { + flink.releaseApp(yarnSessionAppId); } @Test @Order(8) - void testStartFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain started application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); + void testStartFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); } @Test @Order(9) - @SneakyThrows - void testRestartAndCancelFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain restarted application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - applicationsPage.cancelApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); + void testCancelFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); } @Test @Order(10) - void testDeleteFlinkApplicationOnYarnPerJobMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); - } - - @Test - @Order(11) - void testCreateFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_SESSION, - applicationName) - .flinkVersion(flinkName) - .flinkSql(Constants.TEST_FLINK_SQL) - .flinkCluster(flinkClusterName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); - } - - @Test - @Order(12) - void testReleaseFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); - } - - @Test - @Order(13) - void testStartFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); - } - - @Test - @Order(14) - @SneakyThrows - void testRestartAndCancelFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain restarted application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - applicationsPage.cancelApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); - } - - @Test - @Order(15) void testDeleteFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - applicationsPage.deleteApplication(applicationName); - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); + flink.deleteApp(yarnSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnSessionJobName)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL119OnK8sTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL119OnK8sTest.java new file mode 100644 index 0000000000..4bc8fadbdb --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL119OnK8sTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.19-on-k8s/docker-compose.yaml") +public class FlinkSQL119OnK8sTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.19.0"; + + private static final String flinkHome = "/flink-1.19.0"; + + private static final String clusterName = "flink_1.19.0_k8s_cluster_e2e"; + + private static final String k8sAppJobName = "flink119e2etest"; + + private static final String k8sSessionJobName = "flink119session"; + + private static final String flinkImage = "flink:1.19.0-scala_2.12-java8"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + private static long k8sAppId; + + private static long k8sSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterBody.put("flinkImage", flinkImage); + clusterBody.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnKubernetesApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sAppJobName, FlinkApiConstants.DEPLOY_K8S_APPLICATION, versionId); + body.put("k8sNamespace", k8sNamespace); + body.put("flinkImage", flinkImage); + k8sAppId = flink.createApp(body); + assertThat(k8sAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnKubernetesApplicationMode() { + flink.releaseApp(k8sAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnKubernetesApplicationMode() { + flink.deleteApp(k8sAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnKubernetesSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sSessionJobName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("flinkClusterId", clusterId); + k8sSessionAppId = flink.createApp(body); + assertThat(k8sSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnKubernetesSessionMode() { + flink.releaseApp(k8sSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnKubernetesSessionMode() { + flink.deleteApp(k8sSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL119OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL119OnYarnTest.java new file mode 100644 index 0000000000..ce9f9d01ca --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL119OnYarnTest.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.19-on-yarn/docker-compose.yaml") +public class FlinkSQL119OnYarnTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.19.0"; + + private static final String flinkHome = "/flink-1.19.0"; + + private static final String clusterName = "flink_1.19.0_cluster_e2e"; + + private static final String yarnAppJobName = "flink-119-e2e-test"; + + private static final String yarnSessionJobName = "flink-119-session-e2e"; + + private static long versionId; + + private static long clusterId; + + private static long yarnAppId; + + private static long yarnSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnYarnApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnAppJobName, FlinkApiConstants.DEPLOY_YARN_APPLICATION, versionId); + yarnAppId = flink.createApp(body); + assertThat(yarnAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnYarnApplicationMode() { + flink.releaseApp(yarnAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnYarnApplicationMode() { + flink.deleteApp(yarnAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnYarnSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnSessionJobName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("flinkClusterId", clusterId); + yarnSessionAppId = flink.createApp(body); + assertThat(yarnSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnYarnSessionMode() { + flink.releaseApp(yarnSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnYarnSessionMode() { + flink.deleteApp(yarnSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL120OnK8sTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL120OnK8sTest.java new file mode 100644 index 0000000000..383cabcab7 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL120OnK8sTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-1.20-on-k8s/docker-compose.yaml") +public class FlinkSQL120OnK8sTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-1.20.1"; + + private static final String flinkHome = "/flink-1.20.1"; + + private static final String clusterName = "flink_1.20.1_k8s_cluster_e2e"; + + private static final String k8sAppJobName = "flink120e2etest"; + + private static final String k8sSessionJobName = "flink120session"; + + private static final String flinkImage = "flink:1.20.1-scala_2.12-java8"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + private static long k8sAppId; + + private static long k8sSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterBody.put("flinkImage", flinkImage); + clusterBody.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnKubernetesApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sAppJobName, FlinkApiConstants.DEPLOY_K8S_APPLICATION, versionId); + body.put("k8sNamespace", k8sNamespace); + body.put("flinkImage", flinkImage); + k8sAppId = flink.createApp(body); + assertThat(k8sAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnKubernetesApplicationMode() { + flink.releaseApp(k8sAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnKubernetesApplicationMode() { + flink.deleteApp(k8sAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnKubernetesSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sSessionJobName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("flinkClusterId", clusterId); + k8sSessionAppId = flink.createApp(body); + assertThat(k8sSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnKubernetesSessionMode() { + flink.releaseApp(k8sSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnKubernetesSessionMode() { + flink.deleteApp(k8sSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL120OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL120OnYarnTest.java index 39147c962c..1f5f887779 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL120OnYarnTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL120OnYarnTest.java @@ -17,253 +17,144 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.common.Constants; -import org.apache.streampark.e2e.pages.flink.ApacheFlinkPage; -import org.apache.streampark.e2e.pages.flink.FlinkHomePage; -import org.apache.streampark.e2e.pages.flink.applications.ApplicationForm; -import org.apache.streampark.e2e.pages.flink.applications.ApplicationsPage; -import org.apache.streampark.e2e.pages.flink.clusters.ClusterDetailForm; -import org.apache.streampark.e2e.pages.flink.clusters.FlinkClustersPage; -import org.apache.streampark.e2e.pages.flink.clusters.YarnSessionForm; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; -import lombok.SneakyThrows; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.time.Duration; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@StreamPark(composeFiles = "docker/flink-1.20-on-yarn/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/flink-1.20-on-yarn/docker-compose.yaml") public class FlinkSQL120OnYarnTest { - public static RemoteWebDriver browser; + public static ApiClient api; + + private static FlinkApiSupport flink; private static final String flinkName = "flink-1.20.1"; private static final String flinkHome = "/flink-1.20.1"; - private static final String applicationName = "flink-120-e2e-test"; + private static final String clusterName = "flink_1.20.1_cluster_e2e"; - private static final String flinkDescription = "description test"; + private static final String yarnAppJobName = "flink-120-e2e-test"; - private static final String flinkClusterName = "flink_1.20.1_cluster_e2e"; - - @BeforeAll - public static void setup() { - FlinkHomePage flinkHomePage = new LoginPage(browser) - .login() - .goToNav(ApacheFlinkPage.class) - .goToTab(FlinkHomePage.class); + private static final String yarnSessionJobName = "flink-120-session-e2e"; - flinkHomePage.createFlinkHome(flinkName, flinkHome, flinkDescription); + private static long versionId; - FlinkClustersPage flinkClustersPage = flinkHomePage.goToNav(ApacheFlinkPage.class) - .goToTab(FlinkClustersPage.class); + private static long clusterId; - flinkClustersPage.createFlinkCluster() - .addCluster(ClusterDetailForm.DeployMode.YARN_SESSION) - .resolveOrder(YarnSessionForm.ResolveOrder.PARENT_FIRST) - .clusterName(flinkClusterName) - .flinkVersion(flinkName) - .submit(); + private static long yarnAppId; - flinkClustersPage.startFlinkCluster(flinkClusterName); + private static long yarnSessionAppId; - flinkClustersPage.goToNav(ApacheFlinkPage.class) - .goToTab(ApplicationsPage.class); + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); } @Test @Order(1) void testCreateFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_APPLICATION, - applicationName) - .flinkVersion(flinkName) - .flinkSql(Constants.TEST_FLINK_SQL) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); + ObjectNode body = + flink.baseSqlAppBody(yarnAppJobName, FlinkApiConstants.DEPLOY_YARN_APPLICATION, versionId); + yarnAppId = flink.createApp(body); + assertThat(yarnAppId).isPositive(); } @Test @Order(2) void testReleaseFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); + flink.releaseApp(yarnAppId); } @Test @Order(3) void testStartFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); } @Test @Order(4) - @SneakyThrows void testCancelFlinkApplicationOnYarnApplicationMode() { - Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain restarted application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - applicationsPage.cancelApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); } @Test @Order(5) void testDeleteFlinkApplicationOnYarnApplicationMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.deleteApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); + flink.deleteApp(yarnAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnAppJobName)) + .isInstanceOf(IllegalStateException.class); } @Test - @Order(11) + @Order(6) void testCreateFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage - .createApplication() - .addApplication( - ApplicationForm.FlinkJobType.FLINK_SQL, - ApplicationForm.DeployMode.YARN_SESSION, - applicationName) - .flinkVersion(flinkName) - .flinkSql(Constants.TEST_FLINK_SQL) - .flinkCluster(flinkClusterName) - .submit(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain newly-created application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(applicationName))); + ObjectNode body = + flink.baseSqlAppBody(yarnSessionJobName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("flinkClusterId", clusterId); + yarnSessionAppId = flink.createApp(body); + assertThat(yarnSessionAppId).isPositive(); } @Test - @Order(12) + @Order(7) void testReleaseFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.releaseApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain released application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESS"))); + flink.releaseApp(yarnSessionAppId); } @Test - @Order(13) + @Order(8) void testStartFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain finished application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("FINISHED"))); + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); } @Test - @Order(14) - @SneakyThrows - void testRestartAndCancelFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - applicationsPage.startApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain restarted application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("RUNNING"))); - - applicationsPage.cancelApplication(applicationName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(applicationsPage.applicationsList) - .as("Applications list should contain canceled application") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("CANCELED"))); + @Order(9) + void testCancelFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); } @Test - @Order(15) + @Order(10) void testDeleteFlinkApplicationOnYarnSessionMode() { - final ApplicationsPage applicationsPage = new ApplicationsPage(browser); - applicationsPage.deleteApplication(applicationName); - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(applicationsPage.applicationsList) - .noneMatch(it -> it.getText().contains(applicationName)); - }); + flink.deleteApp(yarnSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnSessionJobName)) + .isInstanceOf(IllegalStateException.class); } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL200OnK8sTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL200OnK8sTest.java new file mode 100644 index 0000000000..8a59957bf9 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL200OnK8sTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.0-on-k8s/docker-compose.yaml") +public class FlinkSQL200OnK8sTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.0.2"; + + private static final String flinkHome = "/flink-2.0.2"; + + private static final String clusterName = "flink_2.0.2_k8s_cluster_e2e"; + + private static final String k8sAppJobName = "flink200e2etest"; + + private static final String k8sSessionJobName = "flink200session"; + + private static final String flinkImage = "flink:2.0.2-scala_2.12-java11"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + private static long k8sAppId; + + private static long k8sSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterBody.put("flinkImage", flinkImage); + clusterBody.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnKubernetesApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sAppJobName, FlinkApiConstants.DEPLOY_K8S_APPLICATION, versionId); + body.put("k8sNamespace", k8sNamespace); + body.put("flinkImage", flinkImage); + k8sAppId = flink.createApp(body); + assertThat(k8sAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnKubernetesApplicationMode() { + flink.releaseApp(k8sAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnKubernetesApplicationMode() { + flink.deleteApp(k8sAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnKubernetesSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sSessionJobName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("flinkClusterId", clusterId); + k8sSessionAppId = flink.createApp(body); + assertThat(k8sSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnKubernetesSessionMode() { + flink.releaseApp(k8sSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnKubernetesSessionMode() { + flink.deleteApp(k8sSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL200OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL200OnYarnTest.java new file mode 100644 index 0000000000..d909bd1f22 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL200OnYarnTest.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.0-on-yarn/docker-compose.yaml") +public class FlinkSQL200OnYarnTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.0.2"; + + private static final String flinkHome = "/flink-2.0.2"; + + private static final String clusterName = "flink_2.0.2_cluster_e2e"; + + private static final String yarnAppJobName = "flink-200-e2e-test"; + + private static final String yarnSessionJobName = "flink-200-session-e2e"; + + private static long versionId; + + private static long clusterId; + + private static long yarnAppId; + + private static long yarnSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnYarnApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnAppJobName, FlinkApiConstants.DEPLOY_YARN_APPLICATION, versionId); + yarnAppId = flink.createApp(body); + assertThat(yarnAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnYarnApplicationMode() { + flink.releaseApp(yarnAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnYarnApplicationMode() { + flink.deleteApp(yarnAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnYarnSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnSessionJobName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("flinkClusterId", clusterId); + yarnSessionAppId = flink.createApp(body); + assertThat(yarnSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnYarnSessionMode() { + flink.releaseApp(yarnSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnYarnSessionMode() { + flink.deleteApp(yarnSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL210OnK8sTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL210OnK8sTest.java new file mode 100644 index 0000000000..0d6dfe9db3 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL210OnK8sTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.1-on-k8s/docker-compose.yaml") +public class FlinkSQL210OnK8sTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.1.2"; + + private static final String flinkHome = "/flink-2.1.2"; + + private static final String clusterName = "flink_2.1.2_k8s_cluster_e2e"; + + private static final String k8sAppJobName = "flink210e2etest"; + + private static final String k8sSessionJobName = "flink210session"; + + private static final String flinkImage = "flink:2.1.2-scala_2.12-java11"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + private static long k8sAppId; + + private static long k8sSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterBody.put("flinkImage", flinkImage); + clusterBody.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnKubernetesApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sAppJobName, FlinkApiConstants.DEPLOY_K8S_APPLICATION, versionId); + body.put("k8sNamespace", k8sNamespace); + body.put("flinkImage", flinkImage); + k8sAppId = flink.createApp(body); + assertThat(k8sAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnKubernetesApplicationMode() { + flink.releaseApp(k8sAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnKubernetesApplicationMode() { + flink.deleteApp(k8sAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnKubernetesSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sSessionJobName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("flinkClusterId", clusterId); + k8sSessionAppId = flink.createApp(body); + assertThat(k8sSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnKubernetesSessionMode() { + flink.releaseApp(k8sSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnKubernetesSessionMode() { + flink.deleteApp(k8sSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL210OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL210OnYarnTest.java new file mode 100644 index 0000000000..6fa3cc9ef4 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL210OnYarnTest.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.1-on-yarn/docker-compose.yaml") +public class FlinkSQL210OnYarnTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.1.2"; + + private static final String flinkHome = "/flink-2.1.2"; + + private static final String clusterName = "flink_2.1.2_cluster_e2e"; + + private static final String yarnAppJobName = "flink-210-e2e-test"; + + private static final String yarnSessionJobName = "flink-210-session-e2e"; + + private static long versionId; + + private static long clusterId; + + private static long yarnAppId; + + private static long yarnSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnYarnApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnAppJobName, FlinkApiConstants.DEPLOY_YARN_APPLICATION, versionId); + yarnAppId = flink.createApp(body); + assertThat(yarnAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnYarnApplicationMode() { + flink.releaseApp(yarnAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnYarnApplicationMode() { + flink.deleteApp(yarnAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnYarnSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnSessionJobName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("flinkClusterId", clusterId); + yarnSessionAppId = flink.createApp(body); + assertThat(yarnSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnYarnSessionMode() { + flink.releaseApp(yarnSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnYarnSessionMode() { + flink.deleteApp(yarnSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL220OnK8sTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL220OnK8sTest.java new file mode 100644 index 0000000000..d5a94bea3b --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL220OnK8sTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.2-on-k8s/docker-compose.yaml") +public class FlinkSQL220OnK8sTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.2.1"; + + private static final String flinkHome = "/flink-2.2.1"; + + private static final String clusterName = "flink_2.2.1_k8s_cluster_e2e"; + + private static final String k8sAppJobName = "flink220e2etest"; + + private static final String k8sSessionJobName = "flink220session"; + + private static final String flinkImage = "flink:2.2.1-scala_2.12-java11"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + private static long k8sAppId; + + private static long k8sSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterBody.put("flinkImage", flinkImage); + clusterBody.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnKubernetesApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sAppJobName, FlinkApiConstants.DEPLOY_K8S_APPLICATION, versionId); + body.put("k8sNamespace", k8sNamespace); + body.put("flinkImage", flinkImage); + k8sAppId = flink.createApp(body); + assertThat(k8sAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnKubernetesApplicationMode() { + flink.releaseApp(k8sAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnKubernetesApplicationMode() { + flink.deleteApp(k8sAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnKubernetesSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sSessionJobName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("flinkClusterId", clusterId); + k8sSessionAppId = flink.createApp(body); + assertThat(k8sSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnKubernetesSessionMode() { + flink.releaseApp(k8sSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnKubernetesSessionMode() { + flink.deleteApp(k8sSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL220OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL220OnYarnTest.java new file mode 100644 index 0000000000..bf1c14a7a7 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL220OnYarnTest.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.2-on-yarn/docker-compose.yaml") +public class FlinkSQL220OnYarnTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.2.1"; + + private static final String flinkHome = "/flink-2.2.1"; + + private static final String clusterName = "flink_2.2.1_cluster_e2e"; + + private static final String yarnAppJobName = "flink-220-e2e-test"; + + private static final String yarnSessionJobName = "flink-220-session-e2e"; + + private static long versionId; + + private static long clusterId; + + private static long yarnAppId; + + private static long yarnSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnYarnApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnAppJobName, FlinkApiConstants.DEPLOY_YARN_APPLICATION, versionId); + yarnAppId = flink.createApp(body); + assertThat(yarnAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnYarnApplicationMode() { + flink.releaseApp(yarnAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnYarnApplicationMode() { + flink.deleteApp(yarnAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnYarnSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnSessionJobName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("flinkClusterId", clusterId); + yarnSessionAppId = flink.createApp(body); + assertThat(yarnSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnYarnSessionMode() { + flink.releaseApp(yarnSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnYarnSessionMode() { + flink.deleteApp(yarnSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL230OnK8sTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL230OnK8sTest.java new file mode 100644 index 0000000000..f9c6d46260 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL230OnK8sTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.3-on-k8s/docker-compose.yaml") +public class FlinkSQL230OnK8sTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.3.0"; + + private static final String flinkHome = "/flink-2.3.0"; + + private static final String clusterName = "flink_2.3.0_k8s_cluster_e2e"; + + private static final String k8sAppJobName = "flink230e2etest"; + + private static final String k8sSessionJobName = "flink230session"; + + private static final String flinkImage = "flink:2.3.0-scala_2.12-java11"; + + private static final String k8sNamespace = "default"; + + private static long versionId; + + private static long clusterId; + + private static long k8sAppId; + + private static long k8sSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterBody.put("flinkImage", flinkImage); + clusterBody.put("k8sNamespace", k8sNamespace); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(20)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnKubernetesApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sAppJobName, FlinkApiConstants.DEPLOY_K8S_APPLICATION, versionId); + body.put("k8sNamespace", k8sNamespace); + body.put("flinkImage", flinkImage); + k8sAppId = flink.createApp(body); + assertThat(k8sAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnKubernetesApplicationMode() { + flink.releaseApp(k8sAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnKubernetesApplicationMode() { + flink.startApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sAppId); + flink.waitAppState( + k8sAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnKubernetesApplicationMode() { + flink.deleteApp(k8sAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnKubernetesSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(k8sSessionJobName, FlinkApiConstants.DEPLOY_K8S_SESSION, versionId); + body.put("flinkClusterId", clusterId); + k8sSessionAppId = flink.createApp(body); + assertThat(k8sSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnKubernetesSessionMode() { + flink.releaseApp(k8sSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(25)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnKubernetesSessionMode() { + flink.startApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(25)); + flink.cancelApp(k8sSessionAppId); + flink.waitAppState( + k8sSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(25)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnKubernetesSessionMode() { + flink.deleteApp(k8sSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(k8sSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL230OnYarnTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL230OnYarnTest.java new file mode 100644 index 0000000000..f72f4a501d --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/FlinkSQL230OnYarnTest.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.cases; + +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.flink.FlinkApiConstants; +import org.apache.streampark.e2e.core.api.flink.FlinkApiSupport; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@StreamParkApi(composeFiles = "docker/flink-2.3-on-yarn/docker-compose.yaml") +public class FlinkSQL230OnYarnTest { + + public static ApiClient api; + + private static FlinkApiSupport flink; + + private static final String flinkName = "flink-2.3.0"; + + private static final String flinkHome = "/flink-2.3.0"; + + private static final String clusterName = "flink_2.3.0_cluster_e2e"; + + private static final String yarnAppJobName = "flink-230-e2e-test"; + + private static final String yarnSessionJobName = "flink-230-session-e2e"; + + private static long versionId; + + private static long clusterId; + + private static long yarnAppId; + + private static long yarnSessionAppId; + + @BeforeAll + public static void setup() { + api.login(); + flink = new FlinkApiSupport(api); + flink.createFlinkEnv(flinkName, flinkHome, "description test"); + versionId = flink.requireVersionId(flinkName); + + ObjectNode clusterBody = + flink.baseClusterBody(clusterName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + clusterBody.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + clusterId = flink.createCluster(clusterBody); + flink.startCluster(clusterId); + flink.waitClusterState( + clusterId, state -> state == FlinkApiConstants.CLUSTER_RUNNING, Duration.ofMinutes(15)); + } + + @Test + @Order(1) + void testCreateFlinkApplicationOnYarnApplicationMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnAppJobName, FlinkApiConstants.DEPLOY_YARN_APPLICATION, versionId); + yarnAppId = flink.createApp(body); + assertThat(yarnAppId).isPositive(); + } + + @Test + @Order(2) + void testReleaseFlinkApplicationOnYarnApplicationMode() { + flink.releaseApp(yarnAppId); + } + + @Test + @Order(3) + void testStartFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(4) + void testCancelFlinkApplicationOnYarnApplicationMode() { + flink.startApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnAppId); + flink.waitAppState( + yarnAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(5) + void testDeleteFlinkApplicationOnYarnApplicationMode() { + flink.deleteApp(yarnAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnAppJobName)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + @Order(6) + void testCreateFlinkApplicationOnYarnSessionMode() { + ObjectNode body = + flink.baseSqlAppBody(yarnSessionJobName, FlinkApiConstants.DEPLOY_YARN_SESSION, versionId); + body.put("flinkClusterId", clusterId); + yarnSessionAppId = flink.createApp(body); + assertThat(yarnSessionAppId).isPositive(); + } + + @Test + @Order(7) + void testReleaseFlinkApplicationOnYarnSessionMode() { + flink.releaseApp(yarnSessionAppId); + } + + @Test + @Order(8) + void testStartFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_FINISHED, Duration.ofMinutes(20)); + } + + @Test + @Order(9) + void testCancelFlinkApplicationOnYarnSessionMode() { + flink.startApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_RUNNING, Duration.ofMinutes(20)); + flink.cancelApp(yarnSessionAppId); + flink.waitAppState( + yarnSessionAppId, state -> state == FlinkApiConstants.APP_CANCELED, Duration.ofMinutes(20)); + } + + @Test + @Order(10) + void testDeleteFlinkApplicationOnYarnSessionMode() { + flink.deleteApp(yarnSessionAppId); + assertThatThrownBy(() -> flink.requireAppId(yarnSessionJobName)) + .isInstanceOf(IllegalStateException.class); + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/MemberManagementTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/MemberManagementTest.java index db60907905..098ad16ac6 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/MemberManagementTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/MemberManagementTest.java @@ -17,101 +17,101 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.system.MemberManagementPage; -import org.apache.streampark.e2e.pages.system.SystemPage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class MemberManagementTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String existUserName = "test3"; - private static final String existRole = "developer"; @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SystemPage.class) - .goToTab(MemberManagementPage.class); + api.login(); } @Test @Order(1) void testCreateMember() { - final MemberManagementPage memberManagementPage = new MemberManagementPage(browser); - - memberManagementPage.createMember(existUserName, existRole); + ApiResponse response = api.postForm("/member/post", memberParams(existUserName, existRole)); + assertThat(response.isSuccess()).isTrue(); - Awaitility.await() - .untilAsserted( - () -> assertThat(memberManagementPage.memberList) - .as("Member list should contain newly-created member") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(existUserName))); + ApiResponse list = memberList(); + assertThat(api.pageRecordsContain(list, "userName", existUserName)).isTrue(); } @Test @Order(2) void testCreateDuplicateMember() { - final MemberManagementPage memberManagementPage = new MemberManagementPage(browser); - - memberManagementPage.createMember(existUserName, existRole); - - Awaitility.await() - .untilAsserted( - () -> assertThat(memberManagementPage.errorMessage) - .as("Member Duplicated Error message should be displayed") - .extracting(WebElement::getText) - .matches(it -> it.contains("please don't add it again."))); - - memberManagementPage.errorMessageConfirmButton.click(); - memberManagementPage.createMemberForm.buttonCancel.click(); + ApiResponse response = api.postForm("/member/post", memberParams(existUserName, existRole)); + assertThat(response.isSuccess()).isFalse(); + assertThat(response.getMessage()).contains("please don't add it again."); } @Test @Order(3) void testEditMember() { - final MemberManagementPage memberManagementPage = new MemberManagementPage(browser); String anotherRole = "team admin"; + ApiResponse list = memberList(); + Long memberId = + api.findInPageRecords(list, "userName", existUserName) + .map(node -> node.path("id").asLong()) + .orElseThrow(); - memberManagementPage.editMember(existUserName, anotherRole); + Map params = memberParams(existUserName, anotherRole); + params.put("id", String.valueOf(memberId)); - Awaitility.await() - .untilAsserted( - () -> assertThat(memberManagementPage.memberList) - .as("Team list should contain edited team") - .extracting(WebElement::getText) - .anyMatch( - it -> it.contains(existUserName))); + ApiResponse response = api.putForm("/member/update", params); + assertThat(response.isSuccess()).isTrue(); + + list = memberList(); + assertThat(api.pageRecordsContain(list, "userName", existUserName)).isTrue(); } @Test @Order(4) void testDeleteMember() { - final MemberManagementPage memberManagementPage = new MemberManagementPage(browser); + ApiResponse list = memberList(); + Long memberId = + api.findInPageRecords(list, "userName", existUserName) + .map(node -> node.path("id").asLong()) + .orElseThrow(); + + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("id", String.valueOf(memberId)); - memberManagementPage.deleteMember(existUserName); + ApiResponse response = api.deleteForm("/member/delete", params); + assertThat(response.isSuccess()).isTrue(); - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); + list = memberList(); + assertThat(api.pageRecordsContain(list, "userName", existUserName)).isFalse(); + } + + private static ApiResponse memberList() { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("pageNum", "1"); + params.put("pageSize", "100"); + return api.postForm("/member/list", params); + } - assertThat(memberManagementPage.memberList) - .noneMatch(it -> it.getText().contains(existUserName)); - }); + private static Map memberParams(String userName, String roleName) { + Long roleId = api.findRoleIdByName(roleName).orElseThrow(); + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("userName", userName); + params.put("roleId", String.valueOf(roleId)); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ProjectsManagementTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ProjectsManagementTest.java index d6b1b82f60..d696a5cd78 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ProjectsManagementTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/ProjectsManagementTest.java @@ -17,110 +17,122 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.resource.ProjectsPage; -import org.apache.streampark.e2e.pages.resource.ResourcePage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.support.PageFactory; import org.testcontainers.shaded.org.awaitility.Awaitility; import java.time.Duration; +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class ProjectsManagementTest { - private final Duration PROJECT_BUILD_TIMEOUT_MINUTES = Duration.ofMinutes(15); + private static final Duration PROJECT_BUILD_TIMEOUT = Duration.ofMinutes(15); - public static RemoteWebDriver browser; + public static ApiClient api; private static final String projectName = "e2e_test_project"; - private static final String editedProjectName = "e2e_test_project_edited"; - private static final String url = "https://github.com/apache/streampark-quickstart"; - private static final String branch = "dev"; - private static final String buildArgument = "-pl quickstart-flink/quickstart-apacheflink/apacheflinksql_1.17 -am -Dmaven.test.skip=true"; - private static final String description = "e2e test project description"; + private static Long projectId; + @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(ResourcePage.class) - .goToTab(ProjectsPage.class); + api.login(); } @Test @Order(1) void testCreateProject() { - final ProjectsPage projectsPage = new ProjectsPage(browser); - - projectsPage.createProject(projectName, url, branch, buildArgument, description); + ApiResponse response = api.postForm("/project/create", projectParams(projectName)); + assertThat(response.isSuccess()).isTrue(); - Awaitility.await() - .untilAsserted( - () -> assertThat(projectsPage.projectList) - .as("Projects list should contain newly-created project") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(projectName))); + ApiResponse list = projectList(); + assertThat(api.pageRecordsContain(list, "name", projectName)).isTrue(); } @Test @Order(2) void testEditProject() { - final ProjectsPage projectsPage = new ProjectsPage(browser); - projectsPage.editProject(projectName, editedProjectName); - Awaitility.await() - .untilAsserted( - () -> assertThat(projectsPage.projectList) - .as("Projects list should contain edited project") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(editedProjectName))); + ApiResponse list = projectList(); + projectId = + api.findInPageRecords(list, "name", projectName) + .map(node -> node.path("id").asLong()) + .orElseThrow(); + + Map params = projectParams(editedProjectName); + params.put("id", String.valueOf(projectId)); + + ApiResponse response = api.postForm("/project/update", params); + assertThat(response.isSuccess()).isTrue(); + + list = projectList(); + assertThat(api.pageRecordsContain(list, "name", editedProjectName)).isTrue(); } @Test @Order(3) void testBuildProject() { - ProjectsPage projectsPage = new ProjectsPage(browser); + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("id", String.valueOf(projectId)); - projectsPage.buildProject(editedProjectName); + ApiResponse response = api.postForm("/project/build", params); + assertThat(response.isSuccess()).isTrue(); - Awaitility.await().timeout(PROJECT_BUILD_TIMEOUT_MINUTES) + Awaitility.await() + .atMost(PROJECT_BUILD_TIMEOUT) .untilAsserted( () -> { - browser.navigate().refresh(); - PageFactory.initElements(projectsPage.driver, projectsPage); - assertThat(projectsPage.projectList) - .as("Projects list should contain build success project") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("SUCCESSFUL")); + ApiResponse list = projectList(); + JsonNode record = + api.findInPageRecords(list, "name", editedProjectName).orElseThrow(); + assertThat(record.path("buildState").asInt()).isEqualTo(1); }); - } @Test @Order(4) void testDeleteProject() { - final ProjectsPage projectsPage = new ProjectsPage(browser); - projectsPage.deleteProject(editedProjectName); - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - assertThat(projectsPage.projectList) - .noneMatch(it -> it.getText().contains(editedProjectName)); - }); + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("id", String.valueOf(projectId)); + + ApiResponse response = api.postForm("/project/delete", params); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = projectList(); + assertThat(api.pageRecordsContain(list, "name", editedProjectName)).isFalse(); + } + + private static ApiResponse projectList() { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("pageNum", "1"); + params.put("pageSize", "50"); + return api.postForm("/project/list", params); + } + + private static Map projectParams(String name) { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("name", name); + params.put("url", url); + params.put("refs", branch); + params.put("repository", "1"); + params.put("type", "1"); + params.put("buildArgs", buildArgument); + params.put("description", description); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/RoleManagementTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/RoleManagementTest.java index 18f7ace14a..af3c56256d 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/RoleManagementTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/RoleManagementTest.java @@ -17,97 +17,84 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.system.RoleManagementPage; -import org.apache.streampark.e2e.pages.system.SystemPage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class RoleManagementTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String newRoleName = "new_role"; - private static final String newDescription = "new_description"; - private static final String existMenuName = "Apache Flink"; @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SystemPage.class) - .goToTab(RoleManagementPage.class); + api.login(); } @Test @Order(1) void testCreateRole() { - final RoleManagementPage roleManagementPage = new RoleManagementPage(browser); - roleManagementPage.createRole(newRoleName, newDescription, existMenuName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(roleManagementPage.roleList) - .as("Role list should contain newly-created role") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(newRoleName))); + ApiResponse response = api.postForm("/role/post", roleParams(newRoleName, newDescription, existMenuName)); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/role/list", api.params("pageNum", "1", "pageSize", "100")); + assertThat(api.pageRecordsContain(list, "roleName", newRoleName)).isTrue(); } @Test @Order(2) void testCreateDuplicateRole() { - final RoleManagementPage roleManagementPage = new RoleManagementPage(browser); - roleManagementPage.createRole(newRoleName, newDescription, existMenuName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(roleManagementPage.errorMessageList) - .as("Role Name Duplicated Error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains( - "Sorry, the role name already exists"))); - - roleManagementPage.createRoleForm.buttonCancel.click(); + ApiResponse response = api.postForm("/role/post", roleParams(newRoleName, newDescription, existMenuName)); + assertThat(response.isSuccess()).isFalse(); + assertThat(response.getMessage()).contains("Sorry, the role name already exists"); } + @Test @Order(3) void testEditRole() { - final RoleManagementPage roleManagementPage = new RoleManagementPage(browser); - String newEditDescription = newDescription + "_edit"; - String newEditMenuName = "System"; - roleManagementPage.editRole(newRoleName, newEditDescription, newEditMenuName); - - Awaitility.await() - .untilAsserted( - () -> assertThat(roleManagementPage.roleList) - .as("Role list should contain edited role") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(newEditDescription))); + Long roleId = api.findRoleIdByName(newRoleName).orElseThrow(); + + Map params = roleParams(newRoleName, newEditDescription, "System"); + params.put("roleId", String.valueOf(roleId)); + + ApiResponse response = api.putForm("/role/update", params); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/role/list", api.params("pageNum", "1", "pageSize", "100")); + assertThat(api.pageRecordsContain(list, "description", newEditDescription)).isTrue(); } @Test @Order(4) void testDeleteRole() { - final RoleManagementPage roleManagementPage = new RoleManagementPage(browser); + Long roleId = api.findRoleIdByName(newRoleName).orElseThrow(); + ApiResponse response = api.deleteForm("/role/delete", api.params("roleId", String.valueOf(roleId))); + assertThat(response.isSuccess()).isTrue(); - roleManagementPage.deleteRole(newRoleName); + ApiResponse list = api.postForm("/role/list", api.params("pageNum", "1", "pageSize", "100")); + assertThat(api.pageRecordsContain(list, "roleName", newRoleName)).isFalse(); + } - Awaitility.await() - .untilAsserted( - () -> assertThat(roleManagementPage.roleList) - .extracting(WebElement::getText) - .noneMatch(it -> it.contains(newRoleName))); + private static Map roleParams(String roleName, String description, String menuTitle) { + String menuId = api.findMenuIdByTitle(menuTitle).orElseThrow(); + Map params = new LinkedHashMap<>(); + params.put("roleName", roleName); + params.put("description", description); + params.put("menuId", menuId); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/TeamManagementTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/TeamManagementTest.java index 331851b147..2de97c7c90 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/TeamManagementTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/TeamManagementTest.java @@ -17,98 +17,90 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.system.SystemPage; -import org.apache.streampark.e2e.pages.system.TeamManagementPage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class TeamManagementTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String newTeamName = "test_new_team"; - private static final String newTeamDescription = "test_new_team_description"; @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SystemPage.class) - .goToTab(TeamManagementPage.class); + api.login(); } @Test @Order(1) void testCreateTeam() { - final TeamManagementPage teamManagementPage = new TeamManagementPage(browser); - teamManagementPage.createTeam(newTeamName, newTeamDescription); - - Awaitility.await() - .untilAsserted( - () -> assertThat(teamManagementPage.teamList) - .as("Team list should contain newly-created team") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(newTeamName))); + ApiResponse response = api.postForm("/team/post", teamParams(newTeamName, newTeamDescription)); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/team/list", api.params("pageNum", "1", "pageSize", "100")); + assertThat(api.pageRecordsContain(list, "teamName", newTeamName)).isTrue(); } @Test @Order(2) void testCreateDuplicateTeam() { - final TeamManagementPage teamManagementPage = new TeamManagementPage(browser); - teamManagementPage.createTeam(newTeamName, newTeamDescription); - - Awaitility.await() - .untilAsserted( - () -> assertThat(teamManagementPage.errorMessageList) - .as("Team Duplicated Error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("Create team failed."))); - - teamManagementPage.errorMessageConfirmButton.click(); - teamManagementPage.createTeamForm.buttonCancel.click(); + ApiResponse response = api.postForm("/team/post", teamParams(newTeamName, newTeamDescription)); + assertThat(response.isSuccess()).isFalse(); + assertThat(response.getMessage()).contains("Create team failed."); } @Test @Order(3) void testEditTeam() { - final TeamManagementPage teamManagementPage = new TeamManagementPage(browser); String editDescription = "edit_" + newTeamDescription; + ApiResponse list = api.postForm("/team/list", api.params("pageNum", "1", "pageSize", "100")); + Long teamId = + api.findInPageRecords(list, "teamName", newTeamName) + .map(node -> node.path("id").asLong()) + .orElseThrow(); - teamManagementPage.editTeam(newTeamName, editDescription); + Map params = teamParams(newTeamName, editDescription); + params.put("id", String.valueOf(teamId)); - Awaitility.await() - .untilAsserted( - () -> assertThat(teamManagementPage.teamList) - .as("Team list should contain edited team") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(editDescription))); + ApiResponse response = api.putForm("/team/update", params); + assertThat(response.isSuccess()).isTrue(); + + list = api.postForm("/team/list", api.params("pageNum", "1", "pageSize", "100")); + assertThat(api.pageRecordsContain(list, "description", editDescription)).isTrue(); } @Test @Order(4) void testDeleteTeam() { - final TeamManagementPage teamManagementPage = new TeamManagementPage(browser); + ApiResponse list = api.postForm("/team/list", api.params("pageNum", "1", "pageSize", "100")); + Long teamId = + api.findInPageRecords(list, "teamName", newTeamName) + .map(node -> node.path("id").asLong()) + .orElseThrow(); - teamManagementPage.deleteTeam(newTeamName); + ApiResponse response = api.deleteForm("/team/delete", api.params("id", String.valueOf(teamId))); + assertThat(response.isSuccess()).isTrue(); - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); + list = api.postForm("/team/list", api.params("pageNum", "1", "pageSize", "100")); + assertThat(api.pageRecordsContain(list, "teamName", newTeamName)).isFalse(); + } - assertThat(teamManagementPage.teamList) - .noneMatch(it -> it.getText().contains(newTeamName)); - }); + private static Map teamParams(String teamName, String description) { + Map params = new LinkedHashMap<>(); + params.put("teamName", teamName); + params.put("description", description); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/TokenManagementTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/TokenManagementTest.java index 0d1bab7310..61fc017914 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/TokenManagementTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/TokenManagementTest.java @@ -17,100 +17,80 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.system.SystemPage; -import org.apache.streampark.e2e.pages.system.TokenManagementPage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.Keys; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class TokenManagementTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String existUserName = "admin"; - private static final String newTokenDescription = "test_new_token_description"; + private static String createdToken; + private static Long tokenId; + @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SystemPage.class) - .goToTab(TokenManagementPage.class); + api.login(); } @Test @Order(1) void testCreateToken() { - final TokenManagementPage tokenManagementPage = new TokenManagementPage(browser); - tokenManagementPage.createToken(existUserName, newTokenDescription); - - Awaitility.await() - .untilAsserted( - () -> assertThat(tokenManagementPage.tokenList) - .as("Token list should contain newly-created token") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(existUserName))); + ApiResponse response = api.postForm("/token/create", tokenParams()); + assertThat(response.isSuccess()).isTrue(); + createdToken = response.getData().path("token").asText(); + tokenId = response.getData().path("id").asLong(); + + ApiResponse list = api.postForm("/token/list", api.params("pageNum", "1", "pageSize", "50")); + assertThat(api.pageRecordsContain(list, "username", existUserName)).isTrue(); } @Test @Order(2) void testCopyToken() { - final TokenManagementPage tokenManagementPage = new TokenManagementPage(browser); - tokenManagementPage.copyToken(existUserName); - - // put clipboard value into createTokenForm.description - tokenManagementPage.buttonCreateToken.click(); - tokenManagementPage.createTokenForm.inputDescription.sendKeys(Keys.CONTROL, "v"); - String token = tokenManagementPage.createTokenForm.inputDescription.getAttribute("value"); - tokenManagementPage.createTokenForm.buttonCancel.click(); - - Awaitility.await() - .untilAsserted( - () -> assertThat(tokenManagementPage.tokenList) - .as("Clipboard should contain existing token.") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(token))); + ApiResponse list = api.postForm("/token/list", api.params("pageNum", "1", "pageSize", "50")); + String token = + api.findInPageRecords(list, "username", existUserName) + .map(node -> node.path("token").asText()) + .orElseThrow(); + assertThat(token).isEqualTo(createdToken); } @Test @Order(3) void testCreateDuplicateToken() { - final TokenManagementPage tokenManagementPage = new TokenManagementPage(browser); - - tokenManagementPage.createToken(existUserName, newTokenDescription); - - Awaitility.await() - .untilAsserted( - () -> assertThat(browser.findElement(By.tagName("body")).getText()) - .contains(String.format("user %s already has a token", existUserName))); - - tokenManagementPage.createTokenForm.buttonCancel.click(); + ApiResponse response = api.postForm("/token/create", tokenParams()); + assertThat(response.getCode()).isEqualTo(0L); + assertThat(response.getMessage()).contains(String.format("user %s already has a token", existUserName)); } @Test @Order(4) void testDeleteToken() { - final TokenManagementPage teamManagementPage = new TokenManagementPage(browser); - teamManagementPage.deleteToken(existUserName); - - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - assertThat(teamManagementPage.tokenList) - .noneMatch(it -> it.getText().contains(existUserName)); - }); + ApiResponse response = api.deleteForm("/token/delete", api.params("tokenId", String.valueOf(tokenId))); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/token/list", api.params("pageNum", "1", "pageSize", "50")); + assertThat(api.pageRecordsContain(list, "username", existUserName)).isFalse(); + } + + private static Map tokenParams() { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("userId", String.valueOf(api.getUserId())); + params.put("description", newTokenDescription); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/UploadManagementTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/UploadManagementTest.java index 914b88e7b3..91d26a2920 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/UploadManagementTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/UploadManagementTest.java @@ -17,125 +17,122 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.resource.ResourcePage; -import org.apache.streampark.e2e.pages.resource.UploadsPage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class UploadManagementTest { - public static RemoteWebDriver browser; - - private static final String engineType = "Apache Flink"; - - private static final String resourceType = "Jar Library"; + public static ApiClient api; private static final String resourceName = "test-resource"; private static final String mavenPom = - "\n" + - " junit\n" + - " junit\n" + - " 4.13.2\n" + - " test\n" + - ""; + "\n" + + " junit\n" + + " junit\n" + + " 4.13.2\n" + + " test\n" + + ""; private static final String description = "Junit-jar-lib"; @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(ResourcePage.class) - .goToTab(UploadsPage.class); + api.login(); } @Test @Order(1) void testCreateUpload() { - final UploadsPage uploadsPage = new UploadsPage(browser); - uploadsPage.createUpload(engineType, resourceType, resourceName, mavenPom, description); - - Awaitility.await() - .untilAsserted( - () -> assertThat(uploadsPage.resourceList) - .as("Resource list should contain newly-created resource") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(resourceName)) - .anyMatch(it -> it.contains(description)) - .anyMatch(it -> it.contains(resourceType)) - .anyMatch(it -> it.contains(engineType))); + ApiResponse response = api.postForm("/resource/add", resourceParams(resourceName, mavenPom, description)); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = resourceList(); + assertThat(api.pageRecordsContain(list, "resourceName", resourceName)).isTrue(); + assertThat(api.pageRecordsContain(list, "description", description)).isTrue(); } @Test @Order(2) void testCreateDuplicateUpload() { - final UploadsPage uploadsPage = new UploadsPage(browser); - - uploadsPage.createUpload(engineType, resourceType, resourceName, mavenPom, description); - - Awaitility.await() - .untilAsserted( - () -> assertThat(uploadsPage.errorMessageList) - .as("Resource Name Duplicated Error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains( - String.format("the resource %s already exists, please check.", resourceName)))); - - uploadsPage.errorMessageConfirmButton.click(); - uploadsPage.createUploadForm.buttonCancel.click(); + ApiResponse response = api.postForm("/resource/add", resourceParams(resourceName, mavenPom, description)); + assertThat(response.isSuccess()).isFalse(); + assertThat(response.getMessage()) + .contains(String.format("the resource %s already exists, please check.", resourceName)); } @Test @Order(3) void testEditUpload() { - final UploadsPage uploadsPage = new UploadsPage(browser); - browser.navigate().refresh(); - String editDescription = "Kafka-jar-lib"; String editResource = - "\n" + - " org.apache.kafka\n" + - " kafka-clients\n" + - " 3.7.1\n" + - ""; - - uploadsPage.editUpload(engineType, resourceType, resourceName, editResource, - editDescription); - - Awaitility.await() - .untilAsserted( - () -> assertThat(uploadsPage.resourceList) - .as("Resource list should contain edit resource") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(resourceName)) - .anyMatch(it -> it.contains(editDescription)) - .anyMatch(it -> it.contains(resourceType)) - .anyMatch(it -> it.contains(engineType))); + "\n" + + " org.apache.kafka\n" + + " kafka-clients\n" + + " 3.7.1\n" + + ""; + + ApiResponse list = resourceList(); + Long id = + api.findInPageRecords(list, "resourceName", resourceName) + .map(node -> node.path("id").asLong()) + .orElseThrow(); + + Map params = resourceParams(resourceName, editResource, editDescription); + params.put("id", String.valueOf(id)); + + ApiResponse response = api.putForm("/resource/update", params); + assertThat(response.isSuccess()).isTrue(); + + list = resourceList(); + assertThat(api.pageRecordsContain(list, "resourceName", resourceName)).isTrue(); + assertThat(api.pageRecordsContain(list, "description", editDescription)).isTrue(); } @Test @Order(4) void testDeleteUpload() { - final UploadsPage uploadsPage = new UploadsPage(browser); - uploadsPage.deleteUpload(resourceName); - Awaitility.await() - .untilAsserted( - () -> { - browser.navigate().refresh(); - - assertThat(uploadsPage.resourceList) - .noneMatch(it -> it.getText().contains(resourceName)); - }); + ApiResponse list = resourceList(); + Long id = + api.findInPageRecords(list, "resourceName", resourceName) + .map(node -> node.path("id").asLong()) + .orElseThrow(); + + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("id", String.valueOf(id)); + + ApiResponse response = api.deleteForm("/resource/delete", params); + assertThat(response.isSuccess()).isTrue(); + + list = resourceList(); + assertThat(api.pageRecordsContain(list, "resourceName", resourceName)).isFalse(); + } + + private static ApiResponse resourceList() { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("pageNum", "1"); + params.put("pageSize", "50"); + return api.postForm("/resource/page", params); + } + + private static Map resourceParams(String name, String resource, String resourceDescription) { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("engineType", "FLINK"); + params.put("resourceType", "JAR_LIBRARY"); + params.put("resourceName", name); + params.put("resource", resource); + params.put("description", resourceDescription); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/UserManagementTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/UserManagementTest.java index e41262db35..1e16b4af2c 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/UserManagementTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/UserManagementTest.java @@ -17,90 +17,90 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.system.SystemPage; -import org.apache.streampark.e2e.pages.system.UserManagementPage; -import org.apache.streampark.e2e.pages.system.entity.UserManagementStatus; -import org.apache.streampark.e2e.pages.system.entity.UserManagementUserType; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class UserManagementTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String password = "streampark"; - private static final String newUserName = "test_new"; - private static final String newUserEmail = "test@email.com"; @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SystemPage.class) - .goToTab(UserManagementPage.class); + api.login(); } @Test @Order(1) void testCreateUser() { - final UserManagementPage userManagementPage = new UserManagementPage(browser); - userManagementPage.createUser( - newUserName, "test", password, newUserEmail, UserManagementUserType.ADMIN); - - Awaitility.await() - .untilAsserted( - () -> assertThat(userManagementPage.userList) - .as("User list should contain newly-created user") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(newUserName))); + ApiResponse response = api.postForm("/user/post", createUserParams(newUserName, newUserEmail)); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/user/list", api.params("pageNum", "1", "pageSize", "100")); + assertThat(api.pageRecordsContain(list, "username", newUserName)).isTrue(); } @Test @Order(2) void testCreateDuplicateUser() { - final UserManagementPage userManagementPage = new UserManagementPage(browser); - userManagementPage.createUser( - newUserName, "test", password, "test@email.com", UserManagementUserType.ADMIN); - - Awaitility.await() - .untilAsserted( - () -> assertThat(userManagementPage.errorMessageList) - .as("User Name Duplicated Error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains( - "Sorry the username already exists"))); - - userManagementPage.createUserForm.buttonCancel.click(); + ApiResponse response = api.postForm("/user/post", createUserParams(newUserName, newUserEmail)); + assertThat(response.isSuccess()).isFalse(); + assertThat(response.getMessage()).contains("Sorry the username already exists"); } @Test @Order(3) void testEditUser() { - final UserManagementPage userManagementPage = new UserManagementPage(browser); String editEmail = "edit_" + newUserEmail; + ApiResponse list = api.postForm("/user/list", api.params("pageNum", "1", "pageSize", "100")); + Long userId = + api.findInPageRecords(list, "username", newUserName) + .map(node -> node.path("userId").asLong()) + .orElseThrow(); + + Map params = new LinkedHashMap<>(); + params.put("userId", String.valueOf(userId)); + params.put("username", newUserName); + params.put("email", editEmail); + params.put("userType", "ADMIN"); + params.put("status", "LOCKED"); + params.put("sex", "1"); + params.put("nickName", "test"); + + ApiResponse response = api.putForm("/user/update", params); + assertThat(response.isSuccess()).isTrue(); + + list = api.postForm("/user/list", api.params("pageNum", "1", "pageSize", "100")); + assertThat( + api.findInPageRecords(list, "username", newUserName) + .map(node -> node.path("status").asText()) + .orElse("")) + .isEqualTo("LOCKED"); + } - userManagementPage.editUser( - newUserName, editEmail, UserManagementUserType.ADMIN, UserManagementStatus.LOCKED); - - Awaitility.await() - .untilAsserted( - () -> assertThat(userManagementPage.userList) - .as("User list should contain edited user") - .extracting(WebElement::getText) - .anyMatch( - it -> it.contains(UserManagementStatus.LOCKED - .toString().toLowerCase()))); + private static Map createUserParams(String username, String email) { + Map params = new LinkedHashMap<>(); + params.put("username", username); + params.put("nickName", "test"); + params.put("password", password); + params.put("email", email); + params.put("userType", "ADMIN"); + params.put("status", "VALID"); + params.put("sex", "1"); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/VariableManagementTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/VariableManagementTest.java index 0ad2fc5fad..9d15e2fbb8 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/VariableManagementTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/VariableManagementTest.java @@ -17,101 +17,106 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.resource.ResourcePage; -import org.apache.streampark.e2e.pages.resource.VariablesPage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class VariableManagementTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String variableCode = "10000"; - private static final String variableValue = "3306"; - private static final String description = "MySQL default port"; - private static final boolean isNotVisible = true; - @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(ResourcePage.class) - .goToTab(VariablesPage.class); + api.login(); } @Test @Order(1) void testCreateVariable() { - final VariablesPage variablesPage = new VariablesPage(browser); - variablesPage.createVariable(variableCode, variableValue, description, isNotVisible); - - Awaitility.await() - .untilAsserted( - () -> assertThat(variablesPage.variableList) - .as("Variable list should contain newly-created variable") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(variableCode))); + ApiResponse response = api.postForm("/variable/post", variableParams(variableCode, variableValue, description)); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = variableList(); + assertThat(api.pageRecordsContain(list, "variableCode", variableCode)).isTrue(); } @Test @Order(2) void testCreateDuplicateVariable() { - final VariablesPage variablesPage = new VariablesPage(browser); - variablesPage.createVariable(variableCode, variableValue, description, isNotVisible); - - Awaitility.await() - .untilAsserted( - () -> assertThat(variablesPage.errorMessageList) - .as("Variable Code Duplicated Error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains( - "The variable code already exists."))); - - variablesPage.errorMessageConfirmButton.click(); - variablesPage.createVariableForm.buttonCancel.click(); + ApiResponse response = api.postForm("/variable/post", variableParams(variableCode, variableValue, description)); + assertThat(response.isSuccess()).isFalse(); + assertThat(response.getMessage()).contains("The variable code already exists."); } @Test @Order(3) void testEditVariable() { - final VariablesPage variablesPage = new VariablesPage(browser); String editVariableValue = "6379"; String editDescription = "Redis default port"; - variablesPage.editVariable(variableCode, editVariableValue, editDescription, isNotVisible); - Awaitility.await() - .untilAsserted( - () -> assertThat(variablesPage.variableList) - .as("Variable list should contain edited variable") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(editVariableValue)) - .anyMatch(it -> it.contains(editDescription))); + ApiResponse list = variableList(); + Long id = + api.findInPageRecords(list, "variableCode", variableCode) + .map(node -> node.path("id").asLong()) + .orElseThrow(); + + Map params = variableParams(variableCode, editVariableValue, editDescription); + params.put("id", String.valueOf(id)); + + ApiResponse response = api.putForm("/variable/update", params); + assertThat(response.isSuccess()).isTrue(); + + list = variableList(); + assertThat(api.pageRecordsContain(list, "variableValue", editVariableValue)).isTrue(); + assertThat(api.pageRecordsContain(list, "description", editDescription)).isTrue(); } @Test @Order(4) void testDeleteVariable() { - final VariablesPage variablesPage = new VariablesPage(browser); + ApiResponse list = variableList(); + Long id = + api.findInPageRecords(list, "variableCode", variableCode) + .map(node -> node.path("id").asLong()) + .orElseThrow(); + + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("id", String.valueOf(id)); + + ApiResponse response = api.deleteForm("/variable/delete", params); + assertThat(response.isSuccess()).isTrue(); - variablesPage.deleteVariable(variableCode); + list = variableList(); + assertThat(api.pageRecordsContain(list, "variableCode", variableCode)).isFalse(); + } + + private static ApiResponse variableList() { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("pageNum", "1"); + params.put("pageSize", "50"); + return api.postForm("/variable/page", params); + } - Awaitility.await() - .untilAsserted( - () -> assertThat(variablesPage.variableList) - .extracting(WebElement::getText) - .noneMatch(it -> it.contains(variableCode))); + private static Map variableParams(String code, String value, String variableDescription) { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("variableCode", code); + params.put("variableValue", value); + params.put("description", variableDescription); + params.put("desensitization", "true"); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/YarnQueueTest.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/YarnQueueTest.java index 25faeb6cb7..778d19bd26 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/YarnQueueTest.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/cases/YarnQueueTest.java @@ -17,97 +17,94 @@ package org.apache.streampark.e2e.cases; -import org.apache.streampark.e2e.core.StreamPark; -import org.apache.streampark.e2e.pages.LoginPage; -import org.apache.streampark.e2e.pages.setting.SettingPage; -import org.apache.streampark.e2e.pages.setting.YarnQueuePage; +import org.apache.streampark.e2e.core.StreamParkApi; +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.testcontainers.shaded.org.awaitility.Awaitility; + +import java.util.LinkedHashMap; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; -@StreamPark(composeFiles = "docker/basic/docker-compose.yaml") +@StreamParkApi(composeFiles = "docker/basic/docker-compose.yaml") public class YarnQueueTest { - public static RemoteWebDriver browser; + public static ApiClient api; private static final String newQueueLabel = "new_label"; - private static final String editQueueLabel = "edit_label"; - private static final String description = "test_description"; @BeforeAll public static void setup() { - new LoginPage(browser) - .login() - .goToNav(SettingPage.class) - .goToTab(YarnQueuePage.class); + api.login(); } @Test @Order(1) void testYarnQueue() { - final YarnQueuePage queuePage = new YarnQueuePage(browser); - queuePage.createYarnQueue(newQueueLabel, description); - - Awaitility.await() - .untilAsserted( - () -> assertThat(queuePage.yarnQueueList) - .as("Yarn Queue list should contain newly-created item") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(newQueueLabel)) - .anyMatch(it -> it.contains(description))); + Map params = queueParams(newQueueLabel, description); + ApiResponse response = api.postForm("/yarn/queue/create", params); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/yarn/queue/list", api.params("pageNum", "1", "pageSize", "50")); + assertThat(api.pageRecordsContain(list, "queueLabel", newQueueLabel)).isTrue(); + assertThat(api.pageRecordsContain(list, "description", description)).isTrue(); } @Test @Order(2) void testCreateDuplicateYarnQueue() { - final YarnQueuePage queuePage = new YarnQueuePage(browser); - queuePage.createYarnQueue(newQueueLabel, description); - Awaitility.await() - .untilAsserted( - () -> assertThat(queuePage.errorMessageList) - .as("Yarn Queue Duplicated Error message should be displayed") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains("The queue label existed in the current team"))); - - queuePage.createYarnQueueForm.buttonCancel.click(); + Map params = queueParams(newQueueLabel, description); + ApiResponse response = api.postForm("/yarn/queue/create", params); + assertThat(response.isSuccess()).isFalse(); + assertThat(response.getMessage()).contains("The queue label existed in the current team"); } @Test @Order(3) void testEditYarnQueue() { - final YarnQueuePage queuePage = new YarnQueuePage(browser); String editDescription = "edit_" + description; - - queuePage.editYarnQueue(newQueueLabel, editQueueLabel, editDescription); - - Awaitility.await() - .untilAsserted( - () -> assertThat(queuePage.yarnQueueList) - .as("Yarn queue list should contain edited yarn queue") - .extracting(WebElement::getText) - .anyMatch(it -> it.contains(editQueueLabel)) - .anyMatch(it -> it.contains(editDescription))); + Map params = queueParams(editQueueLabel, editDescription); + params.put("id", api.findInPageRecords( + api.postForm("/yarn/queue/list", api.params("pageNum", "1", "pageSize", "50")), + "queueLabel", + newQueueLabel) + .map(node -> String.valueOf(node.path("id").asLong())) + .orElseThrow()); + + ApiResponse response = api.postForm("/yarn/queue/update", params); + assertThat(response.isSuccess()).isTrue(); + + ApiResponse list = api.postForm("/yarn/queue/list", api.params("pageNum", "1", "pageSize", "50")); + assertThat(api.pageRecordsContain(list, "queueLabel", editQueueLabel)).isTrue(); + assertThat(api.pageRecordsContain(list, "description", editDescription)).isTrue(); } @Test @Order(4) void testDeleteYarnQueue() { - final YarnQueuePage queuePage = new YarnQueuePage(browser); - - queuePage.deleteYarnQueue(editQueueLabel); - Awaitility.await() - .untilAsserted( - () -> { - assertThat(queuePage.yarnQueueList) - .noneMatch(it -> it.getText().contains(editQueueLabel)); - }); + ApiResponse list = api.postForm("/yarn/queue/list", api.params("pageNum", "1", "pageSize", "50")); + String id = + api.findInPageRecords(list, "queueLabel", editQueueLabel) + .map(node -> String.valueOf(node.path("id").asLong())) + .orElseThrow(); + + ApiResponse response = api.postForm("/yarn/queue/delete", api.params("id", id)); + assertThat(response.isSuccess()).isTrue(); + + list = api.postForm("/yarn/queue/list", api.params("pageNum", "1", "pageSize", "50")); + assertThat(api.pageRecordsContain(list, "queueLabel", editQueueLabel)).isFalse(); + } + + private static Map queueParams(String queueLabel, String queueDescription) { + Map params = new LinkedHashMap<>(api.teamParams()); + params.put("queueLabel", queueLabel); + params.put("description", queueDescription); + return params; } } diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/applications/ApplicationForm.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/applications/ApplicationForm.java index b9f4ed1e7f..26ea2d2dff 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/applications/ApplicationForm.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/applications/ApplicationForm.java @@ -32,6 +32,8 @@ import java.util.List; +import static org.apache.streampark.e2e.pages.common.CommonFactory.WebElementDeleteAndInput; + @Getter public final class ApplicationForm { @@ -61,6 +63,15 @@ public final class ApplicationForm { @FindBy(xpath = "//div[contains(@codefield, 'yarnSessionClusterId')]//div[contains(@class, 'ant-select-selector')]") public WebElement buttonFlinkClusterDropdown; + @FindBy(xpath = "//div[contains(@codefield, 'k8sSessionClusterId')]//div[contains(@class, 'ant-select-selector')]") + public WebElement buttonK8sSessionClusterDropdown; + + @FindBy(xpath = "//div[contains(@codefield, 'k8sNamespace')]//input") + public WebElement inputK8sNamespace; + + @FindBy(xpath = "//div[contains(@codefield, 'flinkImage')]//input") + public WebElement inputFlinkImage; + @FindBy(className = "ant-select-item-option-content") private List selectFlinkCluster; @@ -241,6 +252,32 @@ public ApplicationForm flinkCluster(String flinkClusterName) { return this; } + @SneakyThrows + public ApplicationForm k8sSessionCluster(String flinkClusterName) { + new Actions(driver).moveToElement(buttonK8sSessionClusterDropdown).build().perform(); + buttonK8sSessionClusterDropdown.click(); + Thread.sleep(Constants.DEFAULT_SLEEP_MILLISECONDS); + selectFlinkCluster.stream() + .filter(e -> e.getText().contains(flinkClusterName)) + .findFirst() + .orElseThrow( + () -> new IllegalArgumentException( + String.format("Flink cluster not found: %s", flinkClusterName))) + .click(); + + return this; + } + + public ApplicationForm k8sNamespace(String k8sNamespace) { + WebElementDeleteAndInput(inputK8sNamespace, k8sNamespace); + return this; + } + + public ApplicationForm flinkImage(String flinkImage) { + WebElementDeleteAndInput(inputFlinkImage, flinkImage); + return this; + } + public ApplicationForm submit() { buttonSubmit.click(); diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/clusters/ClusterDetailForm.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/clusters/ClusterDetailForm.java index c483ee8b62..23e5fae0e2 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/clusters/ClusterDetailForm.java +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/clusters/ClusterDetailForm.java @@ -65,6 +65,14 @@ public T addCluster(DeployMode deployMode) { String.format("Execution Mode not found: %s", deployMode.desc))) .click(); return (T) new YarnSessionForm(this); + case KUBERNETES_SESSION: + selectDeployMode.stream() + .filter(e -> e.getText().equalsIgnoreCase(DeployMode.KUBERNETES_SESSION.desc)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException( + String.format("Execution Mode not found: %s", deployMode.desc))) + .click(); + return (T) new KubernetesSessionForm(this); default: throw new UnsupportedOperationException( String.format("Unknown execution mode: %s", deployMode.desc)); diff --git a/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/clusters/KubernetesSessionForm.java b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/clusters/KubernetesSessionForm.java new file mode 100644 index 0000000000..75901e5637 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/java/org/apache/streampark/e2e/pages/flink/clusters/KubernetesSessionForm.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.pages.flink.clusters; + +import lombok.Getter; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.FindBys; + +import java.util.List; + +import static org.apache.streampark.e2e.pages.common.CommonFactory.WebElementDeleteAndInput; + +@Getter +public class KubernetesSessionForm extends CommonForm { + + public WebDriver driver; + + @FindBy(xpath = "//div[contains(@codefield, 'resolveOrder')]//div[contains(@class, 'ant-select-selector')]") + public WebElement buttonResolveOrderDropdown; + + @FindBys({ + @FindBy(css = "[codefield=resolveOrder]"), + @FindBy(className = "ant-select-item-option-content") + }) + private List selectResolveOrder; + + @FindBy(xpath = "//div[contains(@codefield, 'flinkImage')]//input") + public WebElement inputFlinkImage; + + @FindBy(xpath = "//div[contains(@codefield, 'k8sNamespace')]//input") + public WebElement inputK8sNamespace; + + public KubernetesSessionForm(ClusterDetailForm clusterDetailForm) { + super(clusterDetailForm); + this.driver = clusterDetailForm.driver; + } + + public KubernetesSessionForm resolveOrder(ResolveOrder resolveOrder) { + buttonResolveOrderDropdown.click(); + selectResolveOrder.stream() + .filter(e -> e.getText().equalsIgnoreCase(resolveOrder.desc)) + .findFirst() + .orElseThrow( + () -> new IllegalArgumentException(String.format("Resolve Order not found: %s", resolveOrder.desc))) + .click(); + + return this; + } + + public KubernetesSessionForm flinkImage(String flinkImage) { + WebElementDeleteAndInput(inputFlinkImage, flinkImage); + return this; + } + + public KubernetesSessionForm k8sNamespace(String k8sNamespace) { + WebElementDeleteAndInput(inputK8sNamespace, k8sNamespace); + return this; + } + + @Getter + public enum ResolveOrder { + + PARENT_FIRST("parent-first"), + CHILD_FIRST("child-first"); + + private final String desc; + + ResolveOrder(String desc) { + this.desc = desc; + } + } +} diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.17-on-k8s/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.17-on-k8s/Dockerfile new file mode 100644 index 0000000000..b9db1ebb7d --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.17-on-k8s/Dockerfile @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:1.17.2-scala_2.12-java8 as flink-image +FROM apache/streampark:ci +COPY --from=flink-image /opt/flink /flink-1.17.2 diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.17-on-k8s/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.17-on-k8s/docker-compose.yaml new file mode 100644 index 0000000000..49038299e3 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.17-on-k8s/docker-compose.yaml @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + k3s: + image: rancher/k3s:v1.30.5-k3s1 + hostname: k3s + command: server --disable=traefik --tls-san=k3s --write-kubeconfig-mode=666 + privileged: true + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + networks: + - e2e + volumes: + - k3s-server:/var/lib/rancher/k3s + - k3s-kubeconfig:/output + healthcheck: + test: [ "CMD", "kubectl", "get", "nodes" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark-flink-1.17.2-on-k8s:ci + hostname: streampark + command: + - bash + - -c + - | + mkdir -p /root/.kube + sed 's/127.0.0.1/k3s/g; s/localhost/k3s/g' /output/kubeconfig.yaml > /root/.kube/config + chmod 600 /root/.kube/config + /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + volumes: + - k3s-kubeconfig:/output:ro + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + privileged: true + restart: unless-stopped + depends_on: + k3s: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: + +volumes: + k3s-server: + k3s-kubeconfig: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.18-on-k8s/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.18-on-k8s/Dockerfile new file mode 100644 index 0000000000..240a0310cd --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.18-on-k8s/Dockerfile @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:1.18.1-scala_2.12-java8 as flink-image +FROM apache/streampark:ci +COPY --from=flink-image /opt/flink /flink-1.18.1 diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.18-on-k8s/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.18-on-k8s/docker-compose.yaml new file mode 100644 index 0000000000..53c85c41e5 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.18-on-k8s/docker-compose.yaml @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + k3s: + image: rancher/k3s:v1.30.5-k3s1 + hostname: k3s + command: server --disable=traefik --tls-san=k3s --write-kubeconfig-mode=666 + privileged: true + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + networks: + - e2e + volumes: + - k3s-server:/var/lib/rancher/k3s + - k3s-kubeconfig:/output + healthcheck: + test: [ "CMD", "kubectl", "get", "nodes" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark-flink-1.18.1-on-k8s:ci + hostname: streampark + command: + - bash + - -c + - | + mkdir -p /root/.kube + sed 's/127.0.0.1/k3s/g; s/localhost/k3s/g' /output/kubeconfig.yaml > /root/.kube/config + chmod 600 /root/.kube/config + /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + volumes: + - k3s-kubeconfig:/output:ro + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + privileged: true + restart: unless-stopped + depends_on: + k3s: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: + +volumes: + k3s-server: + k3s-kubeconfig: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-k8s/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-k8s/Dockerfile new file mode 100644 index 0000000000..01dd160bda --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-k8s/Dockerfile @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:1.19.0-scala_2.12-java8 as flink-image +FROM apache/streampark:ci +COPY --from=flink-image /opt/flink /flink-1.19.0 diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-k8s/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-k8s/docker-compose.yaml new file mode 100644 index 0000000000..e0093b4aae --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-k8s/docker-compose.yaml @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + k3s: + image: rancher/k3s:v1.30.5-k3s1 + hostname: k3s + command: server --disable=traefik --tls-san=k3s --write-kubeconfig-mode=666 + privileged: true + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + networks: + - e2e + volumes: + - k3s-server:/var/lib/rancher/k3s + - k3s-kubeconfig:/output + healthcheck: + test: [ "CMD", "kubectl", "get", "nodes" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark-flink-1.19.0-on-k8s:ci + hostname: streampark + command: + - bash + - -c + - | + mkdir -p /root/.kube + sed 's/127.0.0.1/k3s/g; s/localhost/k3s/g' /output/kubeconfig.yaml > /root/.kube/config + chmod 600 /root/.kube/config + /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + volumes: + - k3s-kubeconfig:/output:ro + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + privileged: true + restart: unless-stopped + depends_on: + k3s: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: + +volumes: + k3s-server: + k3s-kubeconfig: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-remote/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-remote/Dockerfile similarity index 100% rename from streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-remote/Dockerfile rename to streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-remote/Dockerfile diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-remote/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-remote/docker-compose.yaml similarity index 98% rename from streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-remote/docker-compose.yaml rename to streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-remote/docker-compose.yaml index be496ff7c0..f4e0bf2bc9 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-remote/docker-compose.yaml +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-remote/docker-compose.yaml @@ -17,7 +17,7 @@ services: jobmanager: - image: flink:1.16.3 + image: flink:1.19.0 command: jobmanager ports: - "8081:8081" @@ -37,7 +37,7 @@ services: retries: 120 taskmanager: - image: flink:1.16.3 + image: flink:1.19.0 depends_on: - jobmanager command: taskmanager diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-yarn/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-yarn/Dockerfile similarity index 78% rename from streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-yarn/Dockerfile rename to streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-yarn/Dockerfile index f123de65d6..110eb91c2c 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-yarn/Dockerfile +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-yarn/Dockerfile @@ -6,7 +6,7 @@ # (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 +# 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, @@ -16,20 +16,17 @@ # FROM apache/streampark:ci as base-image -FROM flink:1.16.3-scala_2.12-java8 as flink-image +FROM flink:1.19.0-scala_2.12-java8 as flink-image FROM eclipse-temurin:11-jdk-jammy as jdk11-image -# Install streampark FROM sbloodys/hadoop:3.3.6 COPY --from=base-image /streampark /streampark RUN sudo chown -R hadoop.hadoop /streampark \ && sed -i "s/hadoop-user-name: hdfs$/hadoop-user-name: hadoop/g" /streampark/conf/config.yaml -# Install Flink -COPY --from=flink-image /opt/flink /flink-1.16.3 -RUN sudo chown -R hadoop.hadoop /flink-1.16.3 +COPY --from=flink-image /opt/flink /flink-1.19.0 +RUN sudo chown -R hadoop.hadoop /flink-1.19.0 -# Bundled JDK 11 from Eclipse Temurin for StreamPark Console (avoid CentOS 7 yum mirror retirement) COPY --from=jdk11-image /opt/java/openjdk /usr/lib/jvm/jdk11 ENV JAVA_HOME=/usr/lib/jvm/jdk11 ENV PATH="${JAVA_HOME}/bin:${PATH}" diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-yarn/docker-compose.config b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-yarn/docker-compose.config similarity index 100% rename from streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-yarn/docker-compose.config rename to streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-yarn/docker-compose.config diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-yarn/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-yarn/docker-compose.yaml similarity index 87% rename from streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-yarn/docker-compose.yaml rename to streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-yarn/docker-compose.yaml index 0412171f46..6e73be2394 100644 --- a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.16-on-yarn/docker-compose.yaml +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.19-on-yarn/docker-compose.yaml @@ -15,7 +15,7 @@ services: namenode: - image: apache/streampark-flink-1.16.3-on-yarn:ci + image: apache/streampark-flink-1.19.0-on-yarn:ci hostname: namenode command: [ "hdfs", "namenode" ] networks: @@ -42,7 +42,7 @@ services: timeout: 5s retries: 120 datanode: - image: apache/streampark-flink-1.16.3-on-yarn:ci + image: apache/streampark-flink-1.19.0-on-yarn:ci hostname: datanode command: [ "hdfs", "datanode" ] networks: @@ -68,7 +68,7 @@ services: namenode: condition: service_healthy resourcemanager: - image: apache/streampark-flink-1.16.3-on-yarn:ci + image: apache/streampark-flink-1.19.0-on-yarn:ci hostname: resourcemanager command: [ "yarn", "resourcemanager" ] networks: @@ -93,7 +93,7 @@ services: timeout: 5s retries: 120 nodemanager: - image: apache/streampark-flink-1.16.3-on-yarn:ci + image: apache/streampark-flink-1.19.0-on-yarn:ci hostname: nodemanager command: [ "yarn", "nodemanager" ] networks: @@ -119,7 +119,7 @@ services: timeout: 5s retries: 120 streampark: - image: apache/streampark-flink-1.16.3-on-yarn:ci + image: apache/streampark-flink-1.19.0-on-yarn:ci hostname: streampark command: - sh @@ -145,14 +145,14 @@ services: stdin_open: true restart: always depends_on: - namenode: - condition: service_healthy - datanode: - condition: service_healthy - resourcemanager: - condition: service_healthy - nodemanager: - condition: service_healthy + namenode: + condition: service_healthy + datanode: + condition: service_healthy + resourcemanager: + condition: service_healthy + nodemanager: + condition: service_healthy healthcheck: test: [ "CMD", "curl", "http://streampark:10000" ] interval: 5s diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.20-on-k8s/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.20-on-k8s/Dockerfile new file mode 100644 index 0000000000..2b42dc0bcf --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.20-on-k8s/Dockerfile @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:1.20.1-scala_2.12-java8 as flink-image +FROM apache/streampark:ci +COPY --from=flink-image /opt/flink /flink-1.20.1 diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.20-on-k8s/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.20-on-k8s/docker-compose.yaml new file mode 100644 index 0000000000..16568a1da8 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-1.20-on-k8s/docker-compose.yaml @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + k3s: + image: rancher/k3s:v1.30.5-k3s1 + hostname: k3s + command: server --disable=traefik --tls-san=k3s --write-kubeconfig-mode=666 + privileged: true + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + networks: + - e2e + volumes: + - k3s-server:/var/lib/rancher/k3s + - k3s-kubeconfig:/output + healthcheck: + test: [ "CMD", "kubectl", "get", "nodes" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark-flink-1.20.1-on-k8s:ci + hostname: streampark + command: + - bash + - -c + - | + mkdir -p /root/.kube + sed 's/127.0.0.1/k3s/g; s/localhost/k3s/g' /output/kubeconfig.yaml > /root/.kube/config + chmod 600 /root/.kube/config + /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + volumes: + - k3s-kubeconfig:/output:ro + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + privileged: true + restart: unless-stopped + depends_on: + k3s: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: + +volumes: + k3s-server: + k3s-kubeconfig: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-k8s/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-k8s/Dockerfile new file mode 100644 index 0000000000..d65940c174 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-k8s/Dockerfile @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:2.0.2-scala_2.12-java11 as flink-image +FROM apache/streampark:ci +COPY --from=flink-image /opt/flink /flink-2.0.2 diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-k8s/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-k8s/docker-compose.yaml new file mode 100644 index 0000000000..da241c3bc4 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-k8s/docker-compose.yaml @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + k3s: + image: rancher/k3s:v1.30.5-k3s1 + hostname: k3s + command: server --disable=traefik --tls-san=k3s --write-kubeconfig-mode=666 + privileged: true + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + networks: + - e2e + volumes: + - k3s-server:/var/lib/rancher/k3s + - k3s-kubeconfig:/output + healthcheck: + test: [ "CMD", "kubectl", "get", "nodes" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark-flink-2.0.2-on-k8s:ci + hostname: streampark + command: + - bash + - -c + - | + mkdir -p /root/.kube + sed 's/127.0.0.1/k3s/g; s/localhost/k3s/g' /output/kubeconfig.yaml > /root/.kube/config + chmod 600 /root/.kube/config + /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + volumes: + - k3s-kubeconfig:/output:ro + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + privileged: true + restart: unless-stopped + depends_on: + k3s: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: + +volumes: + k3s-server: + k3s-kubeconfig: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-remote/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-remote/Dockerfile new file mode 100644 index 0000000000..b1e093fd41 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-remote/Dockerfile @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-remote/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-remote/docker-compose.yaml new file mode 100644 index 0000000000..85f8f93ca6 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-remote/docker-compose.yaml @@ -0,0 +1,90 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + jobmanager: + image: flink:2.0.2 + command: jobmanager + ports: + - "8081:8081" + environment: + - | + FLINK_PROPERTIES= + jobmanager.rpc.address: jobmanager + networks: + - e2e + volumes: + - flink_data:/opt/flink + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:8081" ] + interval: 5s + timeout: 5s + retries: 120 + + taskmanager: + image: flink:2.0.2 + depends_on: + - jobmanager + command: taskmanager + scale: 1 + environment: + - | + FLINK_PROPERTIES= + jobmanager.rpc.address: jobmanager + taskmanager.numberOfTaskSlots: 2 + networks: + - e2e + volumes: + - flink_data:/opt/flink + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:8081" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark:ci + command: bash bin/streampark.sh start_docker + build: + context: ./ + dockerfile: ./Dockerfile + ports: + - 10000:10000 + - 10030:10030 + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + - FLINK_JOBMANAGER_URL=http://jobmanager:8081 + privileged: true + restart: unless-stopped + networks: + - e2e + volumes: + - flink_data:/opt/flink + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:10000" ] + interval: 5s + timeout: 5s + retries: 120 +networks: + e2e: +volumes: + flink_data: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/Dockerfile new file mode 100644 index 0000000000..b23b63a3c4 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/Dockerfile @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:2.0.2-scala_2.12-java11 as flink-image +FROM eclipse-temurin:11-jdk-jammy as jdk11-image + +FROM sbloodys/hadoop:3.3.6 +COPY --from=base-image /streampark /streampark +RUN sudo chown -R hadoop.hadoop /streampark \ + && sed -i "s/hadoop-user-name: hdfs$/hadoop-user-name: hadoop/g" /streampark/conf/config.yaml + +COPY --from=flink-image /opt/flink /flink-2.0.2 +RUN sudo chown -R hadoop.hadoop /flink-2.0.2 + +COPY --from=jdk11-image /opt/java/openjdk /usr/lib/jvm/jdk11 +ENV JAVA_HOME=/usr/lib/jvm/jdk11 +ENV PATH="${JAVA_HOME}/bin:${PATH}" diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/docker-compose.config b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/docker-compose.config new file mode 100644 index 0000000000..441fa11187 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/docker-compose.config @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +HADOOP_HOME=/opt/hadoop +CORE-SITE.XML_fs.default.name=hdfs://namenode +CORE-SITE.XML_fs.defaultFS=hdfs://namenode +HDFS-SITE.XML_dfs.namenode.rpc-address=namenode:8020 +HDFS-SITE.XML_dfs.replication=1 +MAPRED-SITE.XML_mapreduce.framework.name=yarn +MAPRED-SITE.XML_yarn.app.mapreduce.am.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +MAPRED-SITE.XML_mapreduce.map.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +MAPRED-SITE.XML_mapreduce.reduce.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +YARN-SITE.XML_yarn.resourcemanager.hostname=resourcemanager +YARN-SITE.XML_yarn.nodemanager.pmem-check-enabled=false +YARN-SITE.XML_yarn.nodemanager.delete.debug-delay-sec=600 +YARN-SITE.XML_yarn.nodemanager.vmem-check-enabled=false +YARN-SITE.XML_yarn.nodemanager.aux-services=mapreduce_shuffle +YARN-SITE.XML_yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage=99.9 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-applications=10000 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-am-resource-percent=0.1 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.resource-calculator=org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.queues=default +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.capacity=100 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.user-limit-factor=1 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-capacity=100 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-am-resource-percent=0.9 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.state=RUNNING +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.acl_submit_applications=* +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.acl_administer_queue=* +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.node-locality-delay=40 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.queue-mappings= +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.queue-mappings-override.enable=false diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/docker-compose.yaml new file mode 100644 index 0000000000..b813efc752 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.0-on-yarn/docker-compose.yaml @@ -0,0 +1,163 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +services: + namenode: + image: apache/streampark-flink-2.0.2-on-yarn:ci + hostname: namenode + command: [ "hdfs", "namenode" ] + networks: + - e2e + build: + context: ./ + ports: + - "19870:9870" + env_file: + - docker-compose.config + environment: + ENSURE_NAMENODE_DIR: "/tmp/hadoop-root/dfs/name" + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://namenode:9870" ] + interval: 5s + timeout: 5s + retries: 120 + datanode: + image: apache/streampark-flink-2.0.2-on-yarn:ci + hostname: datanode + command: [ "hdfs", "datanode" ] + networks: + - e2e + build: + context: ./ + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://datanode:9864" ] + interval: 5s + timeout: 5s + retries: 120 + depends_on: + namenode: + condition: service_healthy + resourcemanager: + image: apache/streampark-flink-2.0.2-on-yarn:ci + hostname: resourcemanager + command: [ "yarn", "resourcemanager" ] + networks: + - e2e + build: + context: ./ + ports: + - "18088:8088" + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://resourcemanager:8088" ] + interval: 5s + timeout: 5s + retries: 120 + nodemanager: + image: apache/streampark-flink-2.0.2-on-yarn:ci + hostname: nodemanager + command: [ "yarn", "nodemanager" ] + networks: + - e2e + build: + context: ./ + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + depends_on: + resourcemanager: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://nodemanager:8042" ] + interval: 5s + timeout: 5s + retries: 120 + streampark: + image: apache/streampark-flink-2.0.2-on-yarn:ci + hostname: streampark + command: + - sh + - -c + - /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + depends_on: + namenode: + condition: service_healthy + datanode: + condition: service_healthy + resourcemanager: + condition: service_healthy + nodemanager: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-k8s/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-k8s/Dockerfile new file mode 100644 index 0000000000..7a08c56093 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-k8s/Dockerfile @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:2.1.2-scala_2.12-java11 as flink-image +FROM apache/streampark:ci +COPY --from=flink-image /opt/flink /flink-2.1.2 diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-k8s/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-k8s/docker-compose.yaml new file mode 100644 index 0000000000..161bd65526 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-k8s/docker-compose.yaml @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + k3s: + image: rancher/k3s:v1.30.5-k3s1 + hostname: k3s + command: server --disable=traefik --tls-san=k3s --write-kubeconfig-mode=666 + privileged: true + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + networks: + - e2e + volumes: + - k3s-server:/var/lib/rancher/k3s + - k3s-kubeconfig:/output + healthcheck: + test: [ "CMD", "kubectl", "get", "nodes" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark-flink-2.1.2-on-k8s:ci + hostname: streampark + command: + - bash + - -c + - | + mkdir -p /root/.kube + sed 's/127.0.0.1/k3s/g; s/localhost/k3s/g' /output/kubeconfig.yaml > /root/.kube/config + chmod 600 /root/.kube/config + /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + volumes: + - k3s-kubeconfig:/output:ro + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + privileged: true + restart: unless-stopped + depends_on: + k3s: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: + +volumes: + k3s-server: + k3s-kubeconfig: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-remote/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-remote/Dockerfile new file mode 100644 index 0000000000..b1e093fd41 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-remote/Dockerfile @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-remote/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-remote/docker-compose.yaml new file mode 100644 index 0000000000..a1e9ad3fe7 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-remote/docker-compose.yaml @@ -0,0 +1,90 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + jobmanager: + image: flink:2.1.2 + command: jobmanager + ports: + - "8081:8081" + environment: + - | + FLINK_PROPERTIES= + jobmanager.rpc.address: jobmanager + networks: + - e2e + volumes: + - flink_data:/opt/flink + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:8081" ] + interval: 5s + timeout: 5s + retries: 120 + + taskmanager: + image: flink:2.1.2 + depends_on: + - jobmanager + command: taskmanager + scale: 1 + environment: + - | + FLINK_PROPERTIES= + jobmanager.rpc.address: jobmanager + taskmanager.numberOfTaskSlots: 2 + networks: + - e2e + volumes: + - flink_data:/opt/flink + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:8081" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark:ci + command: bash bin/streampark.sh start_docker + build: + context: ./ + dockerfile: ./Dockerfile + ports: + - 10000:10000 + - 10030:10030 + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + - FLINK_JOBMANAGER_URL=http://jobmanager:8081 + privileged: true + restart: unless-stopped + networks: + - e2e + volumes: + - flink_data:/opt/flink + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:10000" ] + interval: 5s + timeout: 5s + retries: 120 +networks: + e2e: +volumes: + flink_data: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/Dockerfile new file mode 100644 index 0000000000..861f40797f --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/Dockerfile @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:2.1.2-scala_2.12-java11 as flink-image +FROM eclipse-temurin:11-jdk-jammy as jdk11-image + +FROM sbloodys/hadoop:3.3.6 +COPY --from=base-image /streampark /streampark +RUN sudo chown -R hadoop.hadoop /streampark \ + && sed -i "s/hadoop-user-name: hdfs$/hadoop-user-name: hadoop/g" /streampark/conf/config.yaml + +COPY --from=flink-image /opt/flink /flink-2.1.2 +RUN sudo chown -R hadoop.hadoop /flink-2.1.2 + +COPY --from=jdk11-image /opt/java/openjdk /usr/lib/jvm/jdk11 +ENV JAVA_HOME=/usr/lib/jvm/jdk11 +ENV PATH="${JAVA_HOME}/bin:${PATH}" diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/docker-compose.config b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/docker-compose.config new file mode 100644 index 0000000000..441fa11187 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/docker-compose.config @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +HADOOP_HOME=/opt/hadoop +CORE-SITE.XML_fs.default.name=hdfs://namenode +CORE-SITE.XML_fs.defaultFS=hdfs://namenode +HDFS-SITE.XML_dfs.namenode.rpc-address=namenode:8020 +HDFS-SITE.XML_dfs.replication=1 +MAPRED-SITE.XML_mapreduce.framework.name=yarn +MAPRED-SITE.XML_yarn.app.mapreduce.am.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +MAPRED-SITE.XML_mapreduce.map.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +MAPRED-SITE.XML_mapreduce.reduce.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +YARN-SITE.XML_yarn.resourcemanager.hostname=resourcemanager +YARN-SITE.XML_yarn.nodemanager.pmem-check-enabled=false +YARN-SITE.XML_yarn.nodemanager.delete.debug-delay-sec=600 +YARN-SITE.XML_yarn.nodemanager.vmem-check-enabled=false +YARN-SITE.XML_yarn.nodemanager.aux-services=mapreduce_shuffle +YARN-SITE.XML_yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage=99.9 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-applications=10000 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-am-resource-percent=0.1 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.resource-calculator=org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.queues=default +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.capacity=100 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.user-limit-factor=1 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-capacity=100 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-am-resource-percent=0.9 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.state=RUNNING +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.acl_submit_applications=* +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.acl_administer_queue=* +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.node-locality-delay=40 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.queue-mappings= +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.queue-mappings-override.enable=false diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/docker-compose.yaml new file mode 100644 index 0000000000..7d4140f33c --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.1-on-yarn/docker-compose.yaml @@ -0,0 +1,163 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +services: + namenode: + image: apache/streampark-flink-2.1.2-on-yarn:ci + hostname: namenode + command: [ "hdfs", "namenode" ] + networks: + - e2e + build: + context: ./ + ports: + - "19870:9870" + env_file: + - docker-compose.config + environment: + ENSURE_NAMENODE_DIR: "/tmp/hadoop-root/dfs/name" + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://namenode:9870" ] + interval: 5s + timeout: 5s + retries: 120 + datanode: + image: apache/streampark-flink-2.1.2-on-yarn:ci + hostname: datanode + command: [ "hdfs", "datanode" ] + networks: + - e2e + build: + context: ./ + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://datanode:9864" ] + interval: 5s + timeout: 5s + retries: 120 + depends_on: + namenode: + condition: service_healthy + resourcemanager: + image: apache/streampark-flink-2.1.2-on-yarn:ci + hostname: resourcemanager + command: [ "yarn", "resourcemanager" ] + networks: + - e2e + build: + context: ./ + ports: + - "18088:8088" + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://resourcemanager:8088" ] + interval: 5s + timeout: 5s + retries: 120 + nodemanager: + image: apache/streampark-flink-2.1.2-on-yarn:ci + hostname: nodemanager + command: [ "yarn", "nodemanager" ] + networks: + - e2e + build: + context: ./ + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + depends_on: + resourcemanager: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://nodemanager:8042" ] + interval: 5s + timeout: 5s + retries: 120 + streampark: + image: apache/streampark-flink-2.1.2-on-yarn:ci + hostname: streampark + command: + - sh + - -c + - /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + depends_on: + namenode: + condition: service_healthy + datanode: + condition: service_healthy + resourcemanager: + condition: service_healthy + nodemanager: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-k8s/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-k8s/Dockerfile new file mode 100644 index 0000000000..6b4763797e --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-k8s/Dockerfile @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:2.2.1-scala_2.12-java11 as flink-image +FROM apache/streampark:ci +COPY --from=flink-image /opt/flink /flink-2.2.1 diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-k8s/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-k8s/docker-compose.yaml new file mode 100644 index 0000000000..af0bf6cb58 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-k8s/docker-compose.yaml @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + k3s: + image: rancher/k3s:v1.30.5-k3s1 + hostname: k3s + command: server --disable=traefik --tls-san=k3s --write-kubeconfig-mode=666 + privileged: true + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + networks: + - e2e + volumes: + - k3s-server:/var/lib/rancher/k3s + - k3s-kubeconfig:/output + healthcheck: + test: [ "CMD", "kubectl", "get", "nodes" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark-flink-2.2.1-on-k8s:ci + hostname: streampark + command: + - bash + - -c + - | + mkdir -p /root/.kube + sed 's/127.0.0.1/k3s/g; s/localhost/k3s/g' /output/kubeconfig.yaml > /root/.kube/config + chmod 600 /root/.kube/config + /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + volumes: + - k3s-kubeconfig:/output:ro + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + privileged: true + restart: unless-stopped + depends_on: + k3s: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: + +volumes: + k3s-server: + k3s-kubeconfig: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-remote/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-remote/Dockerfile new file mode 100644 index 0000000000..b1e093fd41 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-remote/Dockerfile @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-remote/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-remote/docker-compose.yaml new file mode 100644 index 0000000000..a7946a9e57 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-remote/docker-compose.yaml @@ -0,0 +1,90 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + jobmanager: + image: flink:2.2.1 + command: jobmanager + ports: + - "8081:8081" + environment: + - | + FLINK_PROPERTIES= + jobmanager.rpc.address: jobmanager + networks: + - e2e + volumes: + - flink_data:/opt/flink + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:8081" ] + interval: 5s + timeout: 5s + retries: 120 + + taskmanager: + image: flink:2.2.1 + depends_on: + - jobmanager + command: taskmanager + scale: 1 + environment: + - | + FLINK_PROPERTIES= + jobmanager.rpc.address: jobmanager + taskmanager.numberOfTaskSlots: 2 + networks: + - e2e + volumes: + - flink_data:/opt/flink + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:8081" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark:ci + command: bash bin/streampark.sh start_docker + build: + context: ./ + dockerfile: ./Dockerfile + ports: + - 10000:10000 + - 10030:10030 + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + - FLINK_JOBMANAGER_URL=http://jobmanager:8081 + privileged: true + restart: unless-stopped + networks: + - e2e + volumes: + - flink_data:/opt/flink + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:10000" ] + interval: 5s + timeout: 5s + retries: 120 +networks: + e2e: +volumes: + flink_data: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/Dockerfile new file mode 100644 index 0000000000..9b7389f585 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/Dockerfile @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:2.2.1-scala_2.12-java11 as flink-image +FROM eclipse-temurin:11-jdk-jammy as jdk11-image + +FROM sbloodys/hadoop:3.3.6 +COPY --from=base-image /streampark /streampark +RUN sudo chown -R hadoop.hadoop /streampark \ + && sed -i "s/hadoop-user-name: hdfs$/hadoop-user-name: hadoop/g" /streampark/conf/config.yaml + +COPY --from=flink-image /opt/flink /flink-2.2.1 +RUN sudo chown -R hadoop.hadoop /flink-2.2.1 + +COPY --from=jdk11-image /opt/java/openjdk /usr/lib/jvm/jdk11 +ENV JAVA_HOME=/usr/lib/jvm/jdk11 +ENV PATH="${JAVA_HOME}/bin:${PATH}" diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/docker-compose.config b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/docker-compose.config new file mode 100644 index 0000000000..441fa11187 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/docker-compose.config @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +HADOOP_HOME=/opt/hadoop +CORE-SITE.XML_fs.default.name=hdfs://namenode +CORE-SITE.XML_fs.defaultFS=hdfs://namenode +HDFS-SITE.XML_dfs.namenode.rpc-address=namenode:8020 +HDFS-SITE.XML_dfs.replication=1 +MAPRED-SITE.XML_mapreduce.framework.name=yarn +MAPRED-SITE.XML_yarn.app.mapreduce.am.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +MAPRED-SITE.XML_mapreduce.map.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +MAPRED-SITE.XML_mapreduce.reduce.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +YARN-SITE.XML_yarn.resourcemanager.hostname=resourcemanager +YARN-SITE.XML_yarn.nodemanager.pmem-check-enabled=false +YARN-SITE.XML_yarn.nodemanager.delete.debug-delay-sec=600 +YARN-SITE.XML_yarn.nodemanager.vmem-check-enabled=false +YARN-SITE.XML_yarn.nodemanager.aux-services=mapreduce_shuffle +YARN-SITE.XML_yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage=99.9 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-applications=10000 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-am-resource-percent=0.1 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.resource-calculator=org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.queues=default +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.capacity=100 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.user-limit-factor=1 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-capacity=100 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-am-resource-percent=0.9 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.state=RUNNING +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.acl_submit_applications=* +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.acl_administer_queue=* +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.node-locality-delay=40 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.queue-mappings= +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.queue-mappings-override.enable=false diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/docker-compose.yaml new file mode 100644 index 0000000000..5351bb58f0 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.2-on-yarn/docker-compose.yaml @@ -0,0 +1,163 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +services: + namenode: + image: apache/streampark-flink-2.2.1-on-yarn:ci + hostname: namenode + command: [ "hdfs", "namenode" ] + networks: + - e2e + build: + context: ./ + ports: + - "19870:9870" + env_file: + - docker-compose.config + environment: + ENSURE_NAMENODE_DIR: "/tmp/hadoop-root/dfs/name" + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://namenode:9870" ] + interval: 5s + timeout: 5s + retries: 120 + datanode: + image: apache/streampark-flink-2.2.1-on-yarn:ci + hostname: datanode + command: [ "hdfs", "datanode" ] + networks: + - e2e + build: + context: ./ + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://datanode:9864" ] + interval: 5s + timeout: 5s + retries: 120 + depends_on: + namenode: + condition: service_healthy + resourcemanager: + image: apache/streampark-flink-2.2.1-on-yarn:ci + hostname: resourcemanager + command: [ "yarn", "resourcemanager" ] + networks: + - e2e + build: + context: ./ + ports: + - "18088:8088" + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://resourcemanager:8088" ] + interval: 5s + timeout: 5s + retries: 120 + nodemanager: + image: apache/streampark-flink-2.2.1-on-yarn:ci + hostname: nodemanager + command: [ "yarn", "nodemanager" ] + networks: + - e2e + build: + context: ./ + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + depends_on: + resourcemanager: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://nodemanager:8042" ] + interval: 5s + timeout: 5s + retries: 120 + streampark: + image: apache/streampark-flink-2.2.1-on-yarn:ci + hostname: streampark + command: + - sh + - -c + - /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + depends_on: + namenode: + condition: service_healthy + datanode: + condition: service_healthy + resourcemanager: + condition: service_healthy + nodemanager: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-k8s/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-k8s/Dockerfile new file mode 100644 index 0000000000..312d7095eb --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-k8s/Dockerfile @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:2.3.0-scala_2.12-java11 as flink-image +FROM apache/streampark:ci +COPY --from=flink-image /opt/flink /flink-2.3.0 diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-k8s/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-k8s/docker-compose.yaml new file mode 100644 index 0000000000..75d8f34cc3 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-k8s/docker-compose.yaml @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + k3s: + image: rancher/k3s:v1.30.5-k3s1 + hostname: k3s + command: server --disable=traefik --tls-san=k3s --write-kubeconfig-mode=666 + privileged: true + environment: + - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml + - K3S_KUBECONFIG_MODE=666 + networks: + - e2e + volumes: + - k3s-server:/var/lib/rancher/k3s + - k3s-kubeconfig:/output + healthcheck: + test: [ "CMD", "kubectl", "get", "nodes" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark-flink-2.3.0-on-k8s:ci + hostname: streampark + command: + - bash + - -c + - | + mkdir -p /root/.kube + sed 's/127.0.0.1/k3s/g; s/localhost/k3s/g' /output/kubeconfig.yaml > /root/.kube/config + chmod 600 /root/.kube/config + /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + volumes: + - k3s-kubeconfig:/output:ro + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + privileged: true + restart: unless-stopped + depends_on: + k3s: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: + +volumes: + k3s-server: + k3s-kubeconfig: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-remote/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-remote/Dockerfile new file mode 100644 index 0000000000..b1e093fd41 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-remote/Dockerfile @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-remote/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-remote/docker-compose.yaml new file mode 100644 index 0000000000..956b6f90d1 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-remote/docker-compose.yaml @@ -0,0 +1,90 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +services: + jobmanager: + image: flink:2.3.0 + command: jobmanager + ports: + - "8081:8081" + environment: + - | + FLINK_PROPERTIES= + jobmanager.rpc.address: jobmanager + networks: + - e2e + volumes: + - flink_data:/opt/flink + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:8081" ] + interval: 5s + timeout: 5s + retries: 120 + + taskmanager: + image: flink:2.3.0 + depends_on: + - jobmanager + command: taskmanager + scale: 1 + environment: + - | + FLINK_PROPERTIES= + jobmanager.rpc.address: jobmanager + taskmanager.numberOfTaskSlots: 2 + networks: + - e2e + volumes: + - flink_data:/opt/flink + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:8081" ] + interval: 5s + timeout: 5s + retries: 120 + + streampark: + image: apache/streampark:ci + command: bash bin/streampark.sh start_docker + build: + context: ./ + dockerfile: ./Dockerfile + ports: + - 10000:10000 + - 10030:10030 + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + - FLINK_JOBMANAGER_URL=http://jobmanager:8081 + privileged: true + restart: unless-stopped + networks: + - e2e + volumes: + - flink_data:/opt/flink + - ${HOME}/streampark_build_logs:/tmp/streampark/logs/build_logs/ + - /var/run/docker.sock:/var/run/docker.sock + healthcheck: + test: [ "CMD", "curl", "http://localhost:10000" ] + interval: 5s + timeout: 5s + retries: 120 +networks: + e2e: +volumes: + flink_data: diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/Dockerfile b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/Dockerfile new file mode 100644 index 0000000000..9f19f65882 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/Dockerfile @@ -0,0 +1,32 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +FROM apache/streampark:ci as base-image +FROM flink:2.3.0-scala_2.12-java11 as flink-image +FROM eclipse-temurin:11-jdk-jammy as jdk11-image + +FROM sbloodys/hadoop:3.3.6 +COPY --from=base-image /streampark /streampark +RUN sudo chown -R hadoop.hadoop /streampark \ + && sed -i "s/hadoop-user-name: hdfs$/hadoop-user-name: hadoop/g" /streampark/conf/config.yaml + +COPY --from=flink-image /opt/flink /flink-2.3.0 +RUN sudo chown -R hadoop.hadoop /flink-2.3.0 + +COPY --from=jdk11-image /opt/java/openjdk /usr/lib/jvm/jdk11 +ENV JAVA_HOME=/usr/lib/jvm/jdk11 +ENV PATH="${JAVA_HOME}/bin:${PATH}" diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/docker-compose.config b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/docker-compose.config new file mode 100644 index 0000000000..441fa11187 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/docker-compose.config @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +HADOOP_HOME=/opt/hadoop +CORE-SITE.XML_fs.default.name=hdfs://namenode +CORE-SITE.XML_fs.defaultFS=hdfs://namenode +HDFS-SITE.XML_dfs.namenode.rpc-address=namenode:8020 +HDFS-SITE.XML_dfs.replication=1 +MAPRED-SITE.XML_mapreduce.framework.name=yarn +MAPRED-SITE.XML_yarn.app.mapreduce.am.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +MAPRED-SITE.XML_mapreduce.map.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +MAPRED-SITE.XML_mapreduce.reduce.env=HADOOP_MAPRED_HOME=$HADOOP_HOME +YARN-SITE.XML_yarn.resourcemanager.hostname=resourcemanager +YARN-SITE.XML_yarn.nodemanager.pmem-check-enabled=false +YARN-SITE.XML_yarn.nodemanager.delete.debug-delay-sec=600 +YARN-SITE.XML_yarn.nodemanager.vmem-check-enabled=false +YARN-SITE.XML_yarn.nodemanager.aux-services=mapreduce_shuffle +YARN-SITE.XML_yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage=99.9 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-applications=10000 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.maximum-am-resource-percent=0.1 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.resource-calculator=org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.queues=default +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.capacity=100 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.user-limit-factor=1 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-capacity=100 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.maximum-am-resource-percent=0.9 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.state=RUNNING +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.acl_submit_applications=* +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.root.default.acl_administer_queue=* +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.node-locality-delay=40 +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.queue-mappings= +CAPACITY-SCHEDULER.XML_yarn.scheduler.capacity.queue-mappings-override.enable=false diff --git a/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/docker-compose.yaml b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/docker-compose.yaml new file mode 100644 index 0000000000..a7c1343501 --- /dev/null +++ b/streampark-e2e/streampark-e2e-case/src/test/resources/docker/flink-2.3-on-yarn/docker-compose.yaml @@ -0,0 +1,163 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +services: + namenode: + image: apache/streampark-flink-2.3.0-on-yarn:ci + hostname: namenode + command: [ "hdfs", "namenode" ] + networks: + - e2e + build: + context: ./ + ports: + - "19870:9870" + env_file: + - docker-compose.config + environment: + ENSURE_NAMENODE_DIR: "/tmp/hadoop-root/dfs/name" + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://namenode:9870" ] + interval: 5s + timeout: 5s + retries: 120 + datanode: + image: apache/streampark-flink-2.3.0-on-yarn:ci + hostname: datanode + command: [ "hdfs", "datanode" ] + networks: + - e2e + build: + context: ./ + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://datanode:9864" ] + interval: 5s + timeout: 5s + retries: 120 + depends_on: + namenode: + condition: service_healthy + resourcemanager: + image: apache/streampark-flink-2.3.0-on-yarn:ci + hostname: resourcemanager + command: [ "yarn", "resourcemanager" ] + networks: + - e2e + build: + context: ./ + ports: + - "18088:8088" + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + healthcheck: + test: [ "CMD", "curl", "http://resourcemanager:8088" ] + interval: 5s + timeout: 5s + retries: 120 + nodemanager: + image: apache/streampark-flink-2.3.0-on-yarn:ci + hostname: nodemanager + command: [ "yarn", "nodemanager" ] + networks: + - e2e + build: + context: ./ + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + depends_on: + resourcemanager: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://nodemanager:8042" ] + interval: 5s + timeout: 5s + retries: 120 + streampark: + image: apache/streampark-flink-2.3.0-on-yarn:ci + hostname: streampark + command: + - sh + - -c + - /streampark/bin/streampark.sh start_docker + networks: + - e2e + build: + context: ./ + ports: + - "20000:10000" + environment: + - SPRING_PROFILES_ACTIVE=h2 + - TZ=Asia/Shanghai + env_file: + - docker-compose.config + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + tty: true + stdin_open: true + restart: always + depends_on: + namenode: + condition: service_healthy + datanode: + condition: service_healthy + resourcemanager: + condition: service_healthy + nodemanager: + condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "http://streampark:10000" ] + interval: 5s + timeout: 5s + retries: 120 + +networks: + e2e: diff --git a/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/StreamParkApi.java b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/StreamParkApi.java new file mode 100644 index 0000000000..4f031b9219 --- /dev/null +++ b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/StreamParkApi.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.core; + +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Marks API integration tests that exercise StreamPark REST endpoints without Selenium. */ +@Inherited +@Testcontainers +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@TestMethodOrder(OrderAnnotation.class) +@ExtendWith(StreamParkApiExtension.class) +public @interface StreamParkApi { + + String[] composeFiles(); +} diff --git a/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/StreamParkApiExtension.java b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/StreamParkApiExtension.java new file mode 100644 index 0000000000..b78fedc0cd --- /dev/null +++ b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/StreamParkApiExtension.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.core; + +import org.apache.streampark.e2e.core.api.ApiClient; + +import com.google.common.net.HostAndPort; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.testcontainers.containers.ComposeContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.time.Duration; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** Starts StreamPark via docker-compose and injects {@link ApiClient} for REST tests. */ +@Slf4j +final class StreamParkApiExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback { + + private static final int DOCKER_PORT = 10000; + private static final String SERVICE_NAME = "streampark"; + + private final boolean localMode = Objects.equals(System.getProperty("local"), "true"); + private final int localPort = 10001; + + private ComposeContainer compose; + private ApiClient apiClient; + + @Override + public void beforeAll(ExtensionContext context) { + HostAndPort address; + if (localMode) { + address = HostAndPort.fromParts("localhost", localPort); + } else { + compose = createDockerCompose(context); + compose.start(); + address = HostAndPort.fromParts("localhost", compose.getServicePort(SERVICE_NAME, DOCKER_PORT)); + } + apiClient = new ApiClient("http://" + address.getHost() + ":" + address.getPort()); + injectApiClient(context.getRequiredTestClass(), apiClient); + } + + @Override + public void afterAll(ExtensionContext context) { + if (compose != null) { + compose.stop(); + } + } + + @Override + public void beforeEach(ExtensionContext context) { + injectApiClient(context.getRequiredTestClass(), apiClient); + Object instance = context.getRequiredTestInstance(); + Stream.of(instance.getClass().getDeclaredFields()) + .filter(field -> !Modifier.isStatic(field.getModifiers())) + .filter(field -> ApiClient.class.isAssignableFrom(field.getType())) + .forEach(field -> setField(instance, field, apiClient)); + } + + private void injectApiClient(Class clazz, ApiClient client) { + Stream.of(clazz.getDeclaredFields()) + .filter(field -> Modifier.isStatic(field.getModifiers())) + .filter(field -> ApiClient.class.isAssignableFrom(field.getType())) + .forEach(field -> setField(null, field, client)); + } + + private void setField(Object target, Field field, Object value) { + try { + field.setAccessible(true); + field.set(target, value); + } catch (IllegalAccessException e) { + log.error("Failed to inject ApiClient to field: {}", field.getName(), e); + } + } + + private ComposeContainer createDockerCompose(ExtensionContext context) { + final Class clazz = context.getRequiredTestClass(); + final StreamParkApi annotation = clazz.getAnnotation(StreamParkApi.class); + final List files = + Stream.of(annotation.composeFiles()) + .map(path -> StreamParkApi.class.getClassLoader().getResource(path)) + .filter(Objects::nonNull) + .map(URL::getPath) + .map(File::new) + .collect(Collectors.toList()); + + return new ComposeContainer(files) + .withPull(true) + .withTailChildContainers(true) + .withLocalCompose(true) + .withExposedService( + SERVICE_NAME, + DOCKER_PORT, + Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(300))) + .withLogConsumer(SERVICE_NAME, outputFrame -> log.info(outputFrame.getUtf8String())) + .waitingFor( + SERVICE_NAME, Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(300))); + } +} diff --git a/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/ApiClient.java b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/ApiClient.java new file mode 100644 index 0000000000..e1ae8bf907 --- /dev/null +++ b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/ApiClient.java @@ -0,0 +1,269 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.core.api; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** HTTP client for StreamPark REST API integration tests. */ +@Slf4j +@Getter +public final class ApiClient { + + private static final String ADMIN = "admin"; + private static final String PASSWORD = "streampark"; + + private final String baseUrl; + private final HttpClient httpClient; + private final ObjectMapper objectMapper; + + private String token; + private Long teamId; + private Long userId; + + public ApiClient(String baseUrl) { + this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; + this.httpClient = + HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build(); + this.objectMapper = new ObjectMapper(); + } + + public ApiResponse login() { + Map params = new LinkedHashMap<>(); + params.put("username", ADMIN); + params.put("password", PASSWORD); + ApiResponse response = postForm("/passport/signin", params, false); + if (!response.isSuccess() || response.getData() == null) { + throw new IllegalStateException("Login failed: " + response.text()); + } + JsonNode data = response.getData(); + this.token = data.path("token").asText(null); + JsonNode user = data.path("user"); + if (!user.isMissingNode()) { + this.userId = user.path("userId").isNull() ? null : user.path("userId").asLong(); + this.teamId = + user.path("lastTeamId").isNull() ? null : user.path("lastTeamId").asLong(); + } + if (token == null || teamId == null) { + throw new IllegalStateException("Login response missing token or teamId: " + response.text()); + } + return response; + } + + public ApiResponse postForm(String path, Map params) { + return postForm(path, params, true); + } + + public ApiResponse postForm(String path, Map params, boolean withAuth) { + return exchange("POST", path, "application/x-www-form-urlencoded", encodeForm(params), withAuth); + } + + public ApiResponse postJson(String path, String jsonBody) { + return exchange("POST", path, "application/json", jsonBody, true); + } + + public ApiResponse putForm(String path, Map params) { + return exchange("PUT", path, "application/x-www-form-urlencoded", encodeForm(params), true); + } + + public ApiResponse deleteForm(String path, Map params) { + return exchange("DELETE", path, "application/x-www-form-urlencoded", encodeForm(params), true); + } + + public Map teamParams() { + Map params = new LinkedHashMap<>(); + params.put("teamId", String.valueOf(teamId)); + return params; + } + + public Map params(String... keyValues) { + Map params = new LinkedHashMap<>(); + for (int i = 0; i + 1 < keyValues.length; i += 2) { + params.put(keyValues[i], keyValues[i + 1]); + } + return params; + } + + public ObjectNode objectNode() { + return objectMapper.createObjectNode(); + } + + public String writeJson(ObjectNode node) { + try { + return objectMapper.writeValueAsString(node); + } catch (IOException e) { + throw new IllegalStateException("Failed to serialize JSON", e); + } + } + + public Optional findRecordId(ArrayNode records, String field, String expected) { + for (JsonNode record : records) { + if (expected.equals(record.path(field).asText())) { + return Optional.of(record.path("id").asLong()); + } + } + return Optional.empty(); + } + + public Optional findUserIdByUsername(String username) { + ApiResponse response = postForm("/user/list", params("pageNum", "1", "pageSize", "100")); + return findInPageRecords(response, "username", username).map(node -> node.path("userId").asLong()); + } + + public Optional findRoleIdByName(String roleName) { + ApiResponse response = postForm("/role/list", params("pageNum", "1", "pageSize", "100")); + return findInPageRecords(response, "roleName", roleName).map(node -> node.path("roleId").asLong()); + } + + public Optional findMenuIdByTitle(String title) { + ApiResponse response = postForm("/menu/list", new HashMap<>()); + if (!response.isSuccess() || response.getData() == null) { + return Optional.empty(); + } + return findMenuIdRecursive(response.getData(), title); + } + + public Optional findInPageRecords(ApiResponse response, String field, String expected) { + JsonNode data = response.getData(); + if (data == null || !data.has("records")) { + return Optional.empty(); + } + for (JsonNode record : data.get("records")) { + if (expected.equals(record.path(field).asText())) { + return Optional.of(record); + } + } + return Optional.empty(); + } + + public boolean pageRecordsContain(ApiResponse response, String field, String expected) { + return findInPageRecords(response, field, expected).isPresent(); + } + + public boolean listContains(ApiResponse response, String field, String expected) { + JsonNode data = response.getData(); + if (data == null || !data.isArray()) { + return false; + } + for (JsonNode item : data) { + if (expected.equals(item.path(field).asText())) { + return true; + } + } + return false; + } + + private Optional findMenuIdRecursive(JsonNode menus, String title) { + if (menus == null) { + return Optional.empty(); + } + if (menus.isArray()) { + for (JsonNode menu : menus) { + Optional found = findMenuIdRecursive(menu, title); + if (found.isPresent()) { + return found; + } + } + return Optional.empty(); + } + if (title.equals(menus.path("title").asText()) || title.equals(menus.path("menuName").asText())) { + return Optional.of(String.valueOf(menus.path("menuId").asLong())); + } + Iterator> fields = menus.fields(); + while (fields.hasNext()) { + JsonNode child = fields.next().getValue(); + Optional found = findMenuIdRecursive(child, title); + if (found.isPresent()) { + return found; + } + } + return Optional.empty(); + } + + private ApiResponse exchange( + String method, String path, String contentType, String body, boolean withAuth) { + try { + HttpRequest.Builder builder = + HttpRequest.newBuilder() + .uri(URI.create(baseUrl + path)) + .timeout(Duration.ofMinutes(5)) + .header("Content-Type", contentType); + if (withAuth) { + Objects.requireNonNull(token, "Call login() before authenticated requests"); + builder.header("Authorization", token); + } + builder.method( + method, + body == null + ? HttpRequest.BodyPublishers.noBody() + : HttpRequest.BodyPublishers.ofString(body)); + HttpResponse httpResponse = + httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString()); + JsonNode root = parseJson(httpResponse.body()); + log.debug("{} {} -> {} {}", method, path, httpResponse.statusCode(), httpResponse.body()); + return new ApiResponse(httpResponse.statusCode(), root); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("HTTP request interrupted: " + path, e); + } catch (IOException e) { + throw new IllegalStateException("HTTP request failed: " + path, e); + } + } + + private JsonNode parseJson(String body) { + if (body == null || body.isBlank()) { + return objectMapper.createObjectNode(); + } + try { + return objectMapper.readTree(body); + } catch (IOException e) { + throw new IllegalStateException("Invalid JSON response: " + body, e); + } + } + + private static String encodeForm(Map params) { + return params.entrySet().stream() + .map( + entry -> urlEncode(entry.getKey()) + "=" + urlEncode(entry.getValue() == null ? "" : entry.getValue())) + .collect(Collectors.joining("&")); + } + + private static String urlEncode(String value) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } +} diff --git a/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/ApiResponse.java b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/ApiResponse.java new file mode 100644 index 0000000000..22e4b6d4a6 --- /dev/null +++ b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/ApiResponse.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.core.api; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Getter; + +/** Parsed {@code RestResponseBody} envelope from a StreamPark API call. */ +@Getter +public final class ApiResponse { + + private static final long CODE_SUCCESS = 200L; + + private final int httpStatus; + private final JsonNode root; + private final Long code; + private final String message; + private final JsonNode data; + + public ApiResponse(int httpStatus, JsonNode root) { + this.httpStatus = httpStatus; + this.root = root; + this.code = root != null && root.hasNonNull("code") ? root.get("code").asLong() : null; + this.message = root != null && root.has("message") && !root.get("message").isNull() + ? root.get("message").asText() + : null; + this.data = root != null && root.has("data") ? root.get("data") : null; + } + + public boolean isSuccess() { + return code != null && code.longValue() == CODE_SUCCESS; + } + + public String text() { + return root == null ? "" : root.toString(); + } +} diff --git a/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/flink/FlinkApiConstants.java b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/flink/FlinkApiConstants.java new file mode 100644 index 0000000000..32c08fbb89 --- /dev/null +++ b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/flink/FlinkApiConstants.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.core.api.flink; + +import lombok.experimental.UtilityClass; + +/** Numeric constants aligned with StreamPark backend enums for API tests. */ +@UtilityClass +public final class FlinkApiConstants { + + public static final int DEPLOY_STANDALONE = 1; + public static final int DEPLOY_YARN_SESSION = 3; + public static final int DEPLOY_YARN_APPLICATION = 4; + public static final int DEPLOY_K8S_SESSION = 5; + public static final int DEPLOY_K8S_APPLICATION = 6; + + public static final int JOB_TYPE_FLINK_SQL = 2; + public static final int APP_TYPE_APACHE_FLINK = 2; + + public static final int RESOLVE_PARENT_FIRST = 0; + public static final int RESOLVE_CHILD_FIRST = 1; + + public static final int CLUSTER_RUNNING = 1; + public static final int CLUSTER_CANCELED = 2; + + public static final int APP_RUNNING = 5; + public static final int APP_CANCELED = 9; + public static final int APP_FINISHED = 10; + + public static final int PIPELINE_SUCCESS = 3; + public static final int RELEASE_DONE = 0; + + /** datagen flink sql for integration tests */ + public static final String TEST_FLINK_SQL = "CREATE TABLE datagen (\n" + + "f_sequence INT,\n" + + "f_random INT,\n" + + "f_random_str STRING,\n" + + "ts AS localtimestamp,\n" + + "WATERMARK FOR ts AS ts\n" + + ") WITH (\n" + + "'connector' = 'datagen',\n" + + "'rows-per-second'='5',\n" + + "'fields.f_sequence.kind'='sequence',\n" + + "'fields.f_sequence.start'='1',\n" + + "'fields.f_sequence.end'='100',\n" + + "'fields.f_random.min'='1',\n" + + "'fields.f_random.max'='100',\n" + + "'fields.f_random_str.length'='10'\n" + + ");\n" + + "\n" + + "CREATE TABLE print_table (\n" + + "f_sequence INT,\n" + + "f_random INT,\n" + + "f_random_str STRING\n" + + ") WITH (\n" + + "'connector' = 'print'\n" + + ");\n" + + "\n" + + "INSERT INTO print_table select f_sequence,f_random,f_random_str from datagen;"; +} diff --git a/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/flink/FlinkApiSupport.java b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/flink/FlinkApiSupport.java new file mode 100644 index 0000000000..c2b0a642da --- /dev/null +++ b/streampark-e2e/streampark-e2e-core/src/main/java/org/apache/streampark/e2e/core/api/flink/FlinkApiSupport.java @@ -0,0 +1,300 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.streampark.e2e.core.api.flink; + +import org.apache.streampark.e2e.core.api.ApiClient; +import org.apache.streampark.e2e.core.api.ApiResponse; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.time.Duration; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.IntPredicate; +import java.util.function.Predicate; + +/** REST helpers for Flink env / cluster / application lifecycle in E2E tests. */ +public final class FlinkApiSupport { + + private static final Duration DEFAULT_POLL = Duration.ofMinutes(15); + private static final Duration POLL_INTERVAL = Duration.ofSeconds(5); + + private final ApiClient api; + + public FlinkApiSupport(ApiClient api) { + this.api = api; + } + + public ApiClient api() { + return api; + } + + public void createFlinkEnv(String flinkName, String flinkHome, String description) { + ObjectNode body = api.objectNode(); + body.put("flinkName", flinkName); + body.put("flinkHome", flinkHome); + body.put("description", description); + ApiResponse response = api.postJson("/flink/env/create", api.writeJson(body)); + if (!response.isSuccess()) { + throw new IllegalStateException("Create flink env failed: " + response.text()); + } + } + + public long requireVersionId(String flinkName) { + ApiResponse response = api.postForm("/flink/env/list", new LinkedHashMap<>()); + if (!response.isSuccess() || response.getData() == null || !response.getData().isArray()) { + throw new IllegalStateException("List flink env failed: " + response.text()); + } + for (JsonNode env : response.getData()) { + if (flinkName.equals(env.path("flinkName").asText())) { + return env.path("id").asLong(); + } + } + throw new IllegalStateException("Flink env not found: " + flinkName); + } + + public long createCluster(ObjectNode body) { + ApiResponse response = api.postJson("/flink/cluster/create", api.writeJson(body)); + if (!response.isSuccess()) { + throw new IllegalStateException("Create cluster failed: " + response.text()); + } + String clusterName = body.path("clusterName").asText(); + return requireClusterId(clusterName); + } + + public long requireClusterId(String clusterName) { + ApiResponse response = api.postForm("/flink/cluster/list", new LinkedHashMap<>()); + if (!response.isSuccess() || response.getData() == null || !response.getData().isArray()) { + throw new IllegalStateException("List cluster failed: " + response.text()); + } + for (JsonNode cluster : response.getData()) { + if (clusterName.equals(cluster.path("clusterName").asText())) { + return cluster.path("id").asLong(); + } + } + throw new IllegalStateException("Cluster not found: " + clusterName); + } + + public void updateCluster(ObjectNode body) { + ApiResponse response = api.postJson("/flink/cluster/update", api.writeJson(body)); + if (!response.isSuccess()) { + throw new IllegalStateException("Update cluster failed: " + response.text()); + } + } + + public void startCluster(long clusterId) { + ApiResponse response = api.postJson("/flink/cluster/start", jsonId(clusterId)); + if (!response.isSuccess()) { + throw new IllegalStateException("Start cluster failed: " + response.text()); + } + } + + public void shutdownCluster(long clusterId) { + ApiResponse response = api.postJson("/flink/cluster/shutdown", jsonId(clusterId)); + if (!response.isSuccess()) { + throw new IllegalStateException("Shutdown cluster failed: " + response.text()); + } + } + + public void deleteCluster(long clusterId) { + ApiResponse response = api.postJson("/flink/cluster/delete", jsonId(clusterId)); + if (!response.isSuccess()) { + throw new IllegalStateException("Delete cluster failed: " + response.text()); + } + } + + public void waitClusterState(long clusterId, IntPredicate stateMatcher, Duration timeout) { + waitUntil( + () -> getClusterState(clusterId), + Optional::isPresent, + cluster -> stateMatcher.test(cluster.get()), + timeout, + "cluster " + clusterId); + } + + public long createApp(ObjectNode body) { + ApiResponse response = api.postJson("/flink/app/create", api.writeJson(body)); + if (!response.isSuccess()) { + throw new IllegalStateException("Create app failed: " + response.text()); + } + return requireAppId(body.path("jobName").asText()); + } + + public long requireAppId(String jobName) { + ApiResponse response = listApps(jobName); + Optional app = api.findInPageRecords(response, "jobName", jobName); + if (app.isEmpty()) { + throw new IllegalStateException("App not found: " + jobName); + } + return app.get().path("id").asLong(); + } + + public void releaseApp(long appId) { + ObjectNode body = api.objectNode(); + body.put("appId", appId); + body.put("forceBuild", false); + ApiResponse response = api.postJson("/flink/pipe/build", api.writeJson(body)); + if (!response.isSuccess() || !response.getData().asBoolean(false)) { + throw new IllegalStateException("Release app failed: " + response.text()); + } + waitUntil( + () -> getPipelineStatus(appId), + Optional::isPresent, + status -> status.get() == FlinkApiConstants.PIPELINE_SUCCESS, + DEFAULT_POLL, + "pipeline for app " + appId); + } + + public void startApp(long appId) { + ObjectNode body = api.objectNode(); + body.put("id", appId); + body.put("teamId", api.getTeamId()); + body.put("restoreOrTriggerSavepoint", false); + body.put("allowNonRestored", false); + ApiResponse response = api.postJson("/flink/app/start", api.writeJson(body)); + if (!response.isSuccess()) { + throw new IllegalStateException("Start app failed: " + response.text()); + } + } + + public void cancelApp(long appId) { + ObjectNode body = api.objectNode(); + body.put("id", appId); + body.put("teamId", api.getTeamId()); + body.put("restoreOrTriggerSavepoint", false); + body.put("drain", false); + ApiResponse response = api.postJson("/flink/app/cancel", api.writeJson(body)); + if (!response.isSuccess()) { + throw new IllegalStateException("Cancel app failed: " + response.text()); + } + } + + public void deleteApp(long appId) { + ObjectNode body = api.objectNode(); + body.put("id", appId); + body.put("teamId", api.getTeamId()); + ApiResponse response = api.postJson("/flink/app/delete", api.writeJson(body)); + if (!response.isSuccess()) { + throw new IllegalStateException("Delete app failed: " + response.text()); + } + } + + public void waitAppState(long appId, IntPredicate stateMatcher, Duration timeout) { + waitUntil( + () -> getAppState(appId), + Optional::isPresent, + state -> stateMatcher.test(state.get()), + timeout, + "app " + appId); + } + + public ObjectNode baseSqlAppBody(String jobName, int deployMode, long versionId) { + ObjectNode body = api.objectNode(); + body.put("teamId", api.getTeamId()); + body.put("jobType", FlinkApiConstants.JOB_TYPE_FLINK_SQL); + body.put("deployMode", deployMode); + body.put("versionId", versionId); + body.put("flinkSql", FlinkApiConstants.TEST_FLINK_SQL); + body.put("appType", FlinkApiConstants.APP_TYPE_APACHE_FLINK); + body.put("jobName", jobName); + body.put("resolveOrder", FlinkApiConstants.RESOLVE_PARENT_FIRST); + return body; + } + + public ObjectNode baseClusterBody(String clusterName, int deployMode, long versionId) { + ObjectNode body = api.objectNode(); + body.put("clusterName", clusterName); + body.put("deployMode", deployMode); + body.put("versionId", versionId); + return body; + } + + private ApiResponse listApps(String jobName) { + Map params = new LinkedHashMap<>(); + params.put("teamId", String.valueOf(api.getTeamId())); + params.put("pageNum", "1"); + params.put("pageSize", "50"); + params.put("jobName", jobName); + return api.postForm("/flink/app/list", params); + } + + private Optional getAppState(long appId) { + ApiResponse response = api.postForm("/flink/app/get", api.params("id", String.valueOf(appId))); + if (!response.isSuccess() || response.getData() == null) { + return Optional.empty(); + } + return Optional.of(response.getData().path("state").asInt(-1)); + } + + private Optional getClusterState(long clusterId) { + ApiResponse response = + api.postForm("/flink/cluster/get", api.params("id", String.valueOf(clusterId))); + if (!response.isSuccess() || response.getData() == null) { + return Optional.empty(); + } + return Optional.of(response.getData().path("clusterState").asInt(-1)); + } + + private Optional getPipelineStatus(long appId) { + Map params = new LinkedHashMap<>(); + params.put("appId", String.valueOf(appId)); + ApiResponse response = api.postForm("/flink/pipe/detail", params); + if (!response.isSuccess() || response.getData() == null) { + return Optional.empty(); + } + JsonNode pipeline = response.getData().path("pipeline"); + if (pipeline.isMissingNode() || pipeline.isNull()) { + return Optional.empty(); + } + return Optional.of(pipeline.path("pipeStatus").asInt(-1)); + } + + private ObjectNode jsonId(long id) { + ObjectNode body = api.objectNode(); + body.put("id", id); + return body; + } + + private static void waitUntil( + java.util.function.Supplier> supplier, + Predicate> present, + Predicate matcher, + Duration timeout, + String label) { + long deadline = System.currentTimeMillis() + timeout.toMillis(); + while (System.currentTimeMillis() < deadline) { + Optional value = supplier.get(); + if (present.test(value) && matcher.test(value.get())) { + return; + } + sleep(POLL_INTERVAL); + } + throw new IllegalStateException("Timeout waiting for " + label); + } + + private static void sleep(Duration duration) { + try { + Thread.sleep(duration.toMillis()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted while waiting", e); + } + } +}