diff --git a/.asf.yaml b/.asf.yaml index abeb08c0b8..1a8f27e5e0 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -46,7 +46,8 @@ github: - E2E - Docs - Frontend Build -# - "Mergeable: milestone-label-check" + - "Mergeable: milestone-label-check" + - "Title Validator" required_pull_request_reviews: dismiss_stale_reviews: true required_approving_review_count: 2 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e4d5c7dc75..bbcc652f7d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -16,30 +16,39 @@ # /.github/ @SbloodyS -/deploy/ @caishunfeng -/dolphinscheduler-alert/ @caishunfeng -/dolphinscheduler-e2e/ @SbloodyS -/dolphinscheduler-api-test/ @SbloodyS -/dolphinscheduler-registry/ @caishunfeng @ruanwenjun +/deploy/ @SbloodyS +/dolphinscheduler-alert/ @SbloodyS /dolphinscheduler-api/ @caishunfeng @SbloodyS -/dolphinscheduler-dao/ @caishunfeng @SbloodyS -/dolphinscheduler-dao/src/main/resources/sql/ @EricGao888 -/dolphinscheduler-common/ @caishunfeng -/dolphinscheduler-standalone-server/ @caishunfeng -/dolphinscheduler-datasource-plugin/ @caishunfeng -/dolphinscheduler-dist/ @caishunfeng -/dolphinscheduler-meter/ @caishunfeng @ruanwenjun @EricGao888 -/dolphinscheduler-scheduler-plugin/ @caishunfeng +/dolphinscheduler-api-test/ @SbloodyS +/dolphinscheduler-authentication/ @ruanwenjun +/dolphinscheduler-bom/ @ruanwenjun +/dolphinscheduler-common/ @SbloodyS +/dolphinscheduler-dao/ @SbloodyS @ruanwenjun +/dolphinscheduler-dao-plugin/ @SbloodyS @ruanwenjun +/dolphinscheduler-data-quality/ @SbloodyS +/dolphinscheduler-datasource-plugin/ @SbloodyS +/dolphinscheduler-dist/ @SbloodyS +/dolphinscheduler-e2e/ @SbloodyS +/dolphinscheduler-extract/ @ruanwenjun /dolphinscheduler-master/ @caishunfeng @SbloodyS @ruanwenjun -/dolphinscheduler-worker/ @caishunfeng @SbloodyS @ruanwenjun +/dolphinscheduler-meter/ @ruanwenjun @EricGao888 +/dolphinscheduler-microbench/ @SbloodyS @ruanwenjun +/dolphinscheduler-registry/ @ruanwenjun +/dolphinscheduler-scheduler-plugin/ @ruanwenjun /dolphinscheduler-service/ @caishunfeng -/dolphinscheduler-extract/ @caishunfeng @ruanwenjun /dolphinscheduler-spi/ @caishunfeng +/dolphinscheduler-standalone-server/ @SbloodyS +/dolphinscheduler-storage-plugin/ @ruanwenjun @SbloodyS /dolphinscheduler-task-plugin/ @caishunfeng @SbloodyS @zhuangchong /dolphinscheduler-tools/ @caishunfeng @SbloodyS @EricGao888 -/script/ @caishunfeng @SbloodyS @EricGao888 /dolphinscheduler-ui/ @songjianet @Amy0104 -/docs/ @EricGao888 +/dolphinscheduler-worker/ @caishunfeng @SbloodyS @ruanwenjun +/dolphinscheduler-yarn-aop/ @ruanwenjun + +/dolphinscheduler-dao/src/main/resources/sql/ @EricGao888 + +/script/ @SbloodyS @EricGao888 +/docs/ @EricGao888 @SbloodyS /licenses/ @EricGao888 /images/ @EricGao888 -/style/ @caishunfeng +/style/ @SbloodyS @EricGao888 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 652e41a3bd..a3fcfc5686 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -31,4 +31,7 @@ This change added tests and can be verified as follows: (or) +## Pull Request Notice +[Pull Request Notice](https://github.com/apache/dolphinscheduler/blob/dev/docs/docs/en/contribute/join/pull-request.md) + If your pull request contain incompatible change, you should also add it to `docs/docs/en/guide/upgrede/incompatible.md` diff --git a/.github/actions/auto-assign-action b/.github/actions/auto-assign-action new file mode 160000 index 0000000000..288f36f8ce --- /dev/null +++ b/.github/actions/auto-assign-action @@ -0,0 +1 @@ +Subproject commit 288f36f8cea1dded676b9c4477a64ff91e50c104 diff --git a/.github/actions/auto-assign/auto-assign.yml b/.github/actions/auto-assign/auto-assign.yml new file mode 100644 index 0000000000..986ed6c4f2 --- /dev/null +++ b/.github/actions/auto-assign/auto-assign.yml @@ -0,0 +1,22 @@ +# +# 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. +# + +# Set addAssignees to 'author' to set the PR creator as the assignee. +addAssignees: author + +# Enable it to run on drafts +runOnDraft: true diff --git a/.github/actions/labeler/labeler.yml b/.github/actions/labeler/labeler.yml index b4776198e2..6bd9b6daf1 100644 --- a/.github/actions/labeler/labeler.yml +++ b/.github/actions/labeler/labeler.yml @@ -18,37 +18,54 @@ backend: - 'dolphinscheduler-alert/**/*' - 'dolphinscheduler-api/**/*' + - 'dolphinscheduler-authentication/**/*' + - 'dolphinscheduler-bom/**/*' - 'dolphinscheduler-common/**/*' - 'dolphinscheduler-dao/**/*' + - 'dolphinscheduler-dao-plugin/**/*' - 'dolphinscheduler-data-quality/**/*' - 'dolphinscheduler-datasource-plugin/**/*' - 'dolphinscheduler-dist/**/*' - - 'dolphinscheduler-master/**/*' - - 'dolphinscheduler-registry/**/*' - 'dolphinscheduler-extract/**/*' + - 'dolphinscheduler-master/**/*' + - 'dolphinscheduler-meter/**/*' + - 'dolphinscheduler-microbench/**/*' + - 'dolphinscheduler-registry/**/*' - 'dolphinscheduler-scheduler-plugin/**/*' - 'dolphinscheduler-service/**/*' - 'dolphinscheduler-spi/**/*' - 'dolphinscheduler-standalone-server/**/*' + - 'dolphinscheduler-storage-plugin/**/*' - 'dolphinscheduler-task-plugin/**/*' - 'dolphinscheduler-tools/**/*' - 'dolphinscheduler-worker/**/*' + - 'dolphinscheduler-yarn-aop/**/*' - 'script/**/*' + - '.github/workflows/backend.yml' document: - 'docs/**/*' + - '**/*.md' CI&CD: - any: ['.github/**/*'] docker: - - any: ['.deploy/**/*'] + - any: ['deploy/docker/**/*'] UI: - any: ['dolphinscheduler-ui/**/*'] + - any: ['.github/workflows/frontend.yml'] e2e: - any: ['dolphinscheduler-e2e/**/*'] + - any: ['dolphinscheduler-api-test/**/*'] + - any: ['.github/workflows/e2e.yml'] + - any: ['.github/workflows/api-test.yml'] + - any: ['.github/workflows/e2e-k8s.yml'] test: - - any: ['dolphinscheduler-api-test/**/*'] + - any: ['**/test/**/*'] + +kubernetes: + - any: ['deploy/kubernetes/**/*'] diff --git a/.github/boring-cyborg.yml b/.github/boring-cyborg.yml new file mode 100644 index 0000000000..7c5418e87d --- /dev/null +++ b/.github/boring-cyborg.yml @@ -0,0 +1,72 @@ +################################################################################ +# 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. +# Details: https://github.com/apps/boring-cyborg + +###### IssueLink Adder ################################################################################################# +# Insert Issue (Jira/Github etc) link in PR description based on the Issue ID in PR title. +insertIssueLinkInPrDescription: + # specify the placeholder for the issue link that should be present in the description + descriptionIssuePlaceholderRegexp: "^fix: (.*)$" + matchers: + # you can have several matches - for different types of issues + # only the first matching entry is replaced + jiraIssueMatch: + # specify the regexp of issue id that you can find in the title of the PR + # the match groups can be used to build the issue id (${1}, ${2}, etc.). + titleIssueIdRegexp: \[(Feature|Fix|Improvement|Test|E2E|Doc-[0-9]+)\] + # the issue link to be added. ${1}, ${2} ... are replaced with the match groups from the + # title match (remember to use quotes) + descriptionIssueLink: "[${1}](https://github.com/apache/dolphinschedule/${1}/)" + +###### Title Validator ################################################################################################# +# Verifies if commit/PR titles match the regexp specified +verifyTitles: + # Regular expression that should be matched by titles of commits or PR + titleRegexp: ^\[DSIP-[0-9]+\].*$|^\[Feature-[0-9]+\].*$|^\[Fix-[0-9]+\].*$|^\[Doc-[0-9]+\].*$|^\[Improvement-[0-9]+\].*$|^\[Test-[0-9]+\].*$|^\[E2E-[0-9]+\].*$|^\[CI].*$|^\[Chore].*$ + # If set to true, it will always check the PR title (as opposed to the individual commits). + alwaysUsePrTitle: true + # If set to true, it will only check the commit in case there is a single commit. + # In case of multiple commits it will check PR title. + # This reflects the standard behaviour of Github that for `Squash & Merge` GitHub + # uses the PR title rather than commit messages for the squashed commit ¯\_(ツ)_/¯ + # For single-commit PRs it takes the squashed commit message from the commit as expected. + # + # If set to false it will check all commit messages. This is useful when you do not squash commits at merge. + validateEitherPrOrSingleCommitTitle: true + # The title the GitHub status should appear from. + statusTitle: "Title Validator" + # A custom message to be displayed when the title passes validation. + successMessage: "Title Validation successful!" + # A custom message to be displayed when the title fails validation. + # Allows insertion of ${type} (commit/PR), ${title} (the title validated) and ${regex} (the titleRegexp above). + failureMessage: "Wrong ${type} title: ${title}" + +# Various Flags to control behaviour of the "Labeler" +labelerFlags: + # If this flag is changed to 'false', labels would only be added when the PR is first created + # and not when existing PR is updated. + # The default is 'true' which means the labels would be added when PR is updated even if they + # were removed by the user + labelOnPRUpdates: true + +# Comment to be posted to welcome users when they open their first PR +firstPRWelcomeComment: > + Thanks for opening this pull request! Please check out our contributing guidelines. (https://github.com/apache/dolphinscheduler/blob/dev/docs/docs/en/contribute/join/pull-request.md) + +# Comment to be posted to congratulate user on their first merged PR +firstPRMergeComment: > + Awesome work, congrats on your first merged pull request! diff --git a/.github/workflows/mergeable.yml b/.github/workflows/mergeable.yml index 8b7bd8799c..835b34bf57 100644 --- a/.github/workflows/mergeable.yml +++ b/.github/workflows/mergeable.yml @@ -14,65 +14,50 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. ---- -#version: 2 on: pull_request: + types: + - opened + - reopened + - synchronize + - labeled + - unlabeled + - milestoned + - demilestoned + name: "Mergeable" + jobs: result: name: "Mergeable: milestone-label-check" runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 10 steps: - - name: Status - run: | - echo "Temporary skipping this check" - -#mergeable: -# # we can not use `pull_request.*` which including event `pull_request.labeled`, according to https://github.com/mergeability/mergeable/issues/643, -# # otherwise mergeable will keep add or remove label endless, we just need this CI act like the default behavior as -# # GitHub action workflow `pull_requests` https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request like, -# # which only trigger runs when a pull_request event's activity type is opened, synchronize, or reopened -# - when: pull_request.opened, pull_request.reopened, pull_request.synchronize -# name: sync-sql-ddl -# validate: -# # Sql files must change synchronize -# - do: dependent -# files: -# - 'dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_h2.sql' -# - 'dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_mysql.sql' -# - 'dolphinscheduler-dao/src/main/resources/sql/dolphinscheduler_postgresql.sql' -# message: 'Sql files not change synchronize' -# # Add labels 'sql not sync' and comment to reviewers if Sql files not change synchronize -# fail: -# - do: comment -# payload: -# body: > -# :warning: This PR do not change database DDL synchronize. -# leave_old_comment: false -# - do: labels -# add: 'sql not sync' -# # Remove labels 'sql not sync' if pass -# pass: -# - do: labels -# delete: 'sql not sync' -# -# - when: pull_request.* -# name: milestone-label-check -# validate: -# - do: milestone -# no_empty: -# enabled: false # Cannot be empty when true. -# message: 'Milestone is required and cannot be empty.' -# - do: label -# and: -# - must_include: -# regex: 'feature|bug|improvement|document|chore|revert' -# message: 'Label must include one of the following: `feature`, `bug`, `improvement`, `document`, `chore`, `revert`' -# - must_include: -# regex: 'ready-to-merge' -# message: 'Please check if there are PRs that already have a `ready-to-merge` label and can be merged, if exists please merge them first.' + - name: Check milestone and labels + uses: actions/github-script@v7 + with: + script: | + const { data } = await github.request("GET /repos/{owner}/{repo}/pulls/{pr}", { + owner: context.repo.owner, + repo: context.repo.repo, + pr: context.payload.pull_request.number + }); + if (data.milestone) { + core.info(`This pull request has a milestone: ${data.milestone.title}`); + } else { + core.setFailed(`A maintainer needs to set the milestone for this pull request.`); + } + let labeledFlag = false; + for (const label of data.labels) { + if (['feature', 'bug', 'improvement', 'document', 'chore', 'DSIP', 'CI&CD', 'revert'].includes(label.name)) { + core.info(`This pull request has a valid label: ${label.name}`); + labeledFlag = true; + break; + } + } + if (!labeledFlag) { + core.setFailed(`A maintainer needs to set a valid label for this pull request.`); + } diff --git a/.github/workflows/pull-request-robot.yml b/.github/workflows/pull-request-target-robot.yml similarity index 80% rename from .github/workflows/pull-request-robot.yml rename to .github/workflows/pull-request-target-robot.yml index 731849e46d..938e55498a 100644 --- a/.github/workflows/pull-request-robot.yml +++ b/.github/workflows/pull-request-target-robot.yml @@ -15,20 +15,21 @@ # limitations under the License. # -name: "pull-request-robot" +name: "pull-request-target-robot" on: pull_request_target: jobs: - labelRobot: + pullRequestTargetRobot: permissions: contents: read pull-requests: write runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: "Checkout ${{ github.ref }}" - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: persist-credentials: false submodules: true @@ -39,3 +40,8 @@ jobs: repo-token: "${{ secrets.GITHUB_TOKEN }}" configuration-path: .github/actions/labeler/labeler.yml sync-labels: true + + - name: "Auto assign in pull request" + uses: ./.github/actions/auto-assign-action + with: + configuration-path: .github/actions/auto-assign/auto-assign.yml diff --git a/.gitignore b/.gitignore index 342f13bd6a..813ad9c4bc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ .target .idea/ !.idea/icon.png +!.idea/vcs.xml .run/ target/ dist/ diff --git a/.gitmodules b/.gitmodules index 3b91677a6a..bb2ed97946 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,3 +21,7 @@ [submodule ".github/actions/translate-on-issue"] path = .github/actions/translate-on-issue url = https://github.com/xingchun-chen/translation-helper +[submodule ".github/actions/auto-assign-action"] + path = .github/actions/auto-assign-action + url = https://github.com/kentaro-m/auto-assign-action.git + branch = 288f36f diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..f30edcd8a1 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/.licenserc.yaml b/.licenserc.yaml index 821be80ea6..91e8fbf866 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -51,5 +51,6 @@ header: - '**/banner.txt' - '.terraform.lock.hcl' - deploy/kubernetes/dolphinscheduler/README.md.gotmpl + - .idea/vcs.xml comment: on-failure diff --git a/deploy/kubernetes/dolphinscheduler/README.md b/deploy/kubernetes/dolphinscheduler/README.md index 4b83f5b160..4a6b880293 100644 --- a/deploy/kubernetes/dolphinscheduler/README.md +++ b/deploy/kubernetes/dolphinscheduler/README.md @@ -14,6 +14,8 @@ Please refer to the [Quick Start in Kubernetes](../../../docs/docs/en/guide/inst |-----|------|---------|-------------| | alert.affinity | object | `{}` | Affinity is a group of affinity scheduling rules. If specified, the pod's scheduling constraints. More info: [node-affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity) | | alert.annotations | object | `{}` | You can use annotations to attach arbitrary non-identifying metadata to objects. Clients such as tools and libraries can retrieve this metadata. | +| alert.customizedConfig | object | `{}` | configure aligned with https://github.com/apache/dolphinscheduler/blob/dev/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/resources/application.yaml | +| alert.enableCustomizedConfig | bool | `false` | enable configure custom config | | alert.enabled | bool | `true` | Enable or disable the Alert-Server component | | alert.env.JAVA_OPTS | string | `"-Xms512m -Xmx512m -Xmn256m"` | The jvm options for alert server | | alert.livenessProbe | object | `{"enabled":true,"failureThreshold":"3","initialDelaySeconds":"30","periodSeconds":"30","successThreshold":"1","timeoutSeconds":"5"}` | Periodic probe of container liveness. Container will be restarted if the probe fails. More info: [container-probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) | @@ -52,6 +54,8 @@ Please refer to the [Quick Start in Kubernetes](../../../docs/docs/en/guide/inst | alert.tolerations | list | `[]` | Tolerations are appended (excluding duplicates) to pods running with this RuntimeClass during admission, effectively unioning the set of nodes tolerated by the pod and the RuntimeClass. | | api.affinity | object | `{}` | Affinity is a group of affinity scheduling rules. If specified, the pod's scheduling constraints. More info: [node-affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity) | | api.annotations | object | `{}` | You can use annotations to attach arbitrary non-identifying metadata to objects. Clients such as tools and libraries can retrieve this metadata. | +| api.customizedConfig | object | `{}` | configure aligned with https://github.com/apache/dolphinscheduler/blob/dev/dolphinscheduler-api/src/main/resources/application.yaml | +| api.enableCustomizedConfig | bool | `false` | enable configure custom config | | api.enabled | bool | `true` | Enable or disable the API-Server component | | api.env.JAVA_OPTS | string | `"-Xms512m -Xmx512m -Xmn256m"` | The jvm options for api server | | api.livenessProbe | object | `{"enabled":true,"failureThreshold":"3","initialDelaySeconds":"30","periodSeconds":"30","successThreshold":"1","timeoutSeconds":"5"}` | Periodic probe of container liveness. Container will be restarted if the probe fails. More info: [container-probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) | @@ -191,6 +195,8 @@ Please refer to the [Quick Start in Kubernetes](../../../docs/docs/en/guide/inst | initImage.pullPolicy | string | `"IfNotPresent"` | Image pull policy. Options: Always, Never, IfNotPresent | | master.affinity | object | `{}` | Affinity is a group of affinity scheduling rules. If specified, the pod's scheduling constraints. More info: [node-affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity) | | master.annotations | object | `{}` | You can use annotations to attach arbitrary non-identifying metadata to objects. Clients such as tools and libraries can retrieve this metadata. | +| master.customizedConfig | object | `{}` | configure aligned with https://github.com/apache/dolphinscheduler/blob/dev/dolphinscheduler-master/src/main/resources/application.yaml | +| master.enableCustomizedConfig | bool | `false` | enable configure custom config | | master.enabled | bool | `true` | Enable or disable the Master component | | master.env.JAVA_OPTS | string | `"-Xms1g -Xmx1g -Xmn512m"` | The jvm options for master server | | master.env.MASTER_DISPATCH_TASK_NUM | string | `"3"` | Master dispatch task number per batch | @@ -297,6 +303,8 @@ Please refer to the [Quick Start in Kubernetes](../../../docs/docs/en/guide/inst | timezone | string | `"Asia/Shanghai"` | World time and date for cities in all time zones | | worker.affinity | object | `{}` | Affinity is a group of affinity scheduling rules. If specified, the pod's scheduling constraints. More info: [node-affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity) | | worker.annotations | object | `{}` | You can use annotations to attach arbitrary non-identifying metadata to objects. Clients such as tools and libraries can retrieve this metadata. | +| worker.customizedConfig | object | `{}` | configure aligned with https://github.com/apache/dolphinscheduler/blob/dev/dolphinscheduler-worker/src/main/resources/application.yaml | +| worker.enableCustomizedConfig | bool | `false` | enable configure custom config | | worker.enabled | bool | `true` | Enable or disable the Worker component | | worker.env.DEFAULT_TENANT_ENABLED | bool | `false` | If set true, will use worker bootstrap user as the tenant to execute task when the tenant is `default`; | | worker.env.WORKER_EXEC_THREADS | string | `"100"` | Worker execute thread number to limit task instances | @@ -315,7 +323,6 @@ Please refer to the [Quick Start in Kubernetes](../../../docs/docs/en/guide/inst | worker.keda.minReplicaCount | int | `0` | Minimum number of workers created by keda | | worker.keda.namespaceLabels | object | `{}` | Keda namespace labels | | worker.keda.pollingInterval | int | `5` | How often KEDA polls the DolphinScheduler DB to report new scale requests to the HPA | -| worker.livenessProbe | object | `{"enabled":true,"failureThreshold":"3","initialDelaySeconds":"30","periodSeconds":"30","successThreshold":"1","timeoutSeconds":"5"}` | Periodic probe of container liveness. Container will be restarted if the probe fails. More info: [container-probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) | | worker.livenessProbe.enabled | bool | `true` | Turn on and off liveness probe | | worker.livenessProbe.failureThreshold | string | `"3"` | Minimum consecutive failures for the probe | | worker.livenessProbe.initialDelaySeconds | string | `"30"` | Delay before liveness probe is initiated | diff --git a/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-alert.yaml b/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-alert.yaml index fd89c1ed15..15bffe06f2 100644 --- a/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-alert.yaml +++ b/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-alert.yaml @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -{{- if and .Values.alert.enabled }} +{{- if .Values.alert.enableCustomizedConfig }} apiVersion: v1 kind: ConfigMap metadata: @@ -23,93 +23,8 @@ metadata: app.kubernetes.io/name: {{ include "dolphinscheduler.fullname" . }}-alert {{- include "dolphinscheduler.alert.labels" . | nindent 4 }} data: - application.yaml: | - spring: - profiles: - active: {{ .Values.datasource.profile }} - jackson: - time-zone: UTC - date-format: "yyyy-MM-dd HH:mm:ss" - banner: - charset: UTF-8 - datasource: - driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://127.0.0.1:5432/dolphinscheduler - username: root - password: root - hikari: - connection-test-query: select 1 - pool-name: DolphinScheduler - - # Mybatis-plus configuration, you don't need to change it - mybatis-plus: - mapper-locations: classpath:org/apache/dolphinscheduler/dao/mapper/*Mapper.xml - type-aliases-package: org.apache.dolphinscheduler.dao.entity - configuration: - cache-enabled: false - call-setters-on-nulls: true - map-underscore-to-camel-case: true - jdbc-type-for-null: NULL - global-config: - db-config: - id-type: auto - banner: false - - server: - port: 50053 - - management: - endpoints: - web: - exposure: - include: health,metrics,prometheus - endpoint: - health: - enabled: true - show-details: always - health: - db: - enabled: true - defaults: - enabled: false - metrics: - tags: - application: ${spring.application.name} - - alert: - port: 50052 - # Mark each alert of alert server if late after x milliseconds as failed. - # Define value is (0 = infinite), and alert server would be waiting alert result. - wait-timeout: 0 - max-heartbeat-interval: 60s - query_alert_threshold: 100 - - registry: - type: zookeeper - zookeeper: - namespace: dolphinscheduler - connect-string: localhost:2181 - retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms - max-retries: 5 - session-timeout: 30s - connection-timeout: 9s - block-until-connected: 600ms - digest: ~ - - metrics: - enabled: true - - # Override by profile - --- - spring: - config: - activate: - on-profile: mysql - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3306/dolphinscheduler - username: root - password: root -{{- end }} +{{- range $path, $config := .Values.alert.customizedConfig }} + {{ $path }}: | +{{ $config | indent 4 -}} +{{- end -}} +{{- end -}} diff --git a/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-api.yaml b/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-api.yaml index f115490c31..211d3dfda9 100644 --- a/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-api.yaml +++ b/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-api.yaml @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -{{- if and .Values.api.enabled }} +{{- if .Values.api.enableCustomizedConfig }} apiVersion: v1 kind: ConfigMap metadata: @@ -23,236 +23,8 @@ metadata: app.kubernetes.io/name: {{ include "dolphinscheduler.fullname" . }}-api {{- include "dolphinscheduler.api.labels" . | nindent 4 }} data: - application.yaml: | - server: - port: 12345 - servlet: - session: - timeout: 120m - context-path: /dolphinscheduler/ - compression: - enabled: true - mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml - jetty: - max-http-form-post-size: 5000000 - accesslog: - enabled: true - custom-format: '%{client}a - %u %t "%r" %s %O %{ms}Tms' - - spring: - profiles: - active: {{ .Values.datasource.profile }} - banner: - charset: UTF-8 - jackson: - time-zone: UTC - date-format: "yyyy-MM-dd HH:mm:ss" - servlet: - multipart: - max-file-size: 1024MB - max-request-size: 1024MB - messages: - basename: i18n/messages - datasource: - driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://127.0.0.1:5432/dolphinscheduler - username: root - password: root - hikari: - connection-test-query: select 1 - pool-name: DolphinScheduler - quartz: - auto-startup: false - job-store-type: jdbc - jdbc: - initialize-schema: never - properties: - org.quartz.jobStore.isClustered: true - org.quartz.jobStore.class: org.springframework.scheduling.quartz.LocalDataSourceJobStore - org.quartz.scheduler.instanceId: AUTO - org.quartz.jobStore.tablePrefix: QRTZ_ - org.quartz.jobStore.acquireTriggersWithinLock: true - org.quartz.scheduler.instanceName: DolphinScheduler - org.quartz.threadPool.class: org.apache.dolphinscheduler.scheduler.quartz.QuartzZeroSizeThreadPool - org.quartz.jobStore.useProperties: false - org.quartz.jobStore.misfireThreshold: 60000 - org.quartz.scheduler.makeSchedulerThreadDaemon: true - org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate - org.quartz.jobStore.clusterCheckinInterval: 5000 - org.quartz.scheduler.batchTriggerAcquisitionMaxCount: 1 - mvc: - pathmatch: - matching-strategy: ANT_PATH_MATCHER - static-path-pattern: /static/** - springdoc: - swagger-ui: - path: /swagger-ui.html - packages-to-scan: org.apache.dolphinscheduler.api - - # Mybatis-plus configuration, you don't need to change it - mybatis-plus: - mapper-locations: classpath:org/apache/dolphinscheduler/dao/mapper/*Mapper.xml - type-aliases-package: org.apache.dolphinscheduler.dao.entity - configuration: - cache-enabled: false - call-setters-on-nulls: true - map-underscore-to-camel-case: true - jdbc-type-for-null: NULL - global-config: - db-config: - id-type: auto - banner: false - - management: - endpoints: - web: - exposure: - include: health,metrics,prometheus - endpoint: - health: - enabled: true - show-details: always - health: - db: - enabled: true - defaults: - enabled: false - metrics: - tags: - application: ${spring.application.name} - - registry: - type: zookeeper - zookeeper: - namespace: dolphinscheduler - connect-string: localhost:2181 - retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms - max-retries: 5 - session-timeout: 60s - connection-timeout: 15s - block-until-connected: 15s - digest: ~ - - api: - audit-enable: false - # Traffic control, if you turn on this config, the maximum number of request/s will be limited. - # global max request number per second - # default tenant-level max request number - traffic-control: - global-switch: false - max-global-qps-rate: 300 - tenant-switch: false - default-tenant-qps-rate: 10 - #customize-tenant-qps-rate: - # eg. - #tenant1: 11 - #tenant2: 20 - python-gateway: - # Weather enable python gateway server or not. The default value is false. - enabled: false - # Authentication token for connection from python api to python gateway server. Should be changed the default value - # when you deploy in public network. - auth-token: jwUDzpLsNKEFER4*a8gruBH_GsAurNxU7A@Xc - # The address of Python gateway server start. Set its value to `0.0.0.0` if your Python API run in different - # between Python gateway server. It could be be specific to other address like `127.0.0.1` or `localhost` - gateway-server-address: 0.0.0.0 - # The port of Python gateway server start. Define which port you could connect to Python gateway server from - # Python API side. - gateway-server-port: 25333 - # The address of Python callback client. - python-address: 127.0.0.1 - # The port of Python callback client. - python-port: 25334 - # Close connection of socket server if no other request accept after x milliseconds. Define value is (0 = infinite), - # and socket server would never close even though no requests accept - connect-timeout: 0 - # Close each active connection of socket server if python program not active after x milliseconds. Define value is - # (0 = infinite), and socket server would never close even though no requests accept - read-timeout: 0 - - metrics: - enabled: true - - security: - authentication: - # Authentication types (supported types: PASSWORD,LDAP,CASDOOR_SSO) - type: PASSWORD - # IF you set type `LDAP`, below config will be effective - ldap: - # ldap server config - urls: ldap://ldap.forumsys.com:389/ - base-dn: dc=example,dc=com - username: cn=read-only-admin,dc=example,dc=com - password: password - user: - # admin userId when you use LDAP login - admin: read-only-admin - identity-attribute: uid - email-attribute: mail - # action when ldap user is not exist (supported types: CREATE,DENY) - not-exist-action: CREATE - ssl: - enable: false - # jks file absolute path && password - trust-store: "/ldapkeystore.jks" - trust-store-password: "password" - casdoor: - user: - admin: "" - oauth2: - enable: false - provider: - github: - authorizationUri: "" - redirectUri: "" - clientId: "" - clientSecret: "" - tokenUri: "" - userInfoUri: "" - callbackUrl: "" - iconUri: "" - provider: github - google: - authorizationUri: "" - redirectUri: "" - clientId: "" - clientSecret: "" - tokenUri: "" - userInfoUri: "" - callbackUrl: "" - iconUri: "" - provider: google - casdoor: - # Your Casdoor server url - endpoint: "" - client-id: "" - client-secret: "" - # The certificate may be multi-line, you can use `|-` for ease - certificate: "" - # Your organization name added in Casdoor - organization-name: "" - # Your application name added in Casdoor - application-name: "" - # Doplhinscheduler login url - redirect-url: "" - - # Override by profile - --- - spring: - config: - activate: - on-profile: mysql - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3306/dolphinscheduler - username: root - password: root - quartz: - properties: - org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate -{{- end }} - - - +{{- range $path, $config := .Values.api.customizedConfig }} + {{ $path }}: | +{{ $config | indent 4 -}} +{{- end -}} +{{- end -}} diff --git a/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-master.yaml b/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-master.yaml index 0a2c8bf2a4..9bcb7dd411 100644 --- a/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-master.yaml +++ b/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-master.yaml @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -{{- if and .Values.master.enabled }} +{{- if .Values.master.enableCustomizedConfig }} apiVersion: v1 kind: ConfigMap metadata: @@ -23,151 +23,8 @@ metadata: app.kubernetes.io/name: {{ include "dolphinscheduler.fullname" . }}-master {{- include "dolphinscheduler.master.labels" . | nindent 4 }} data: - application.yaml: | - spring: - profiles: - active: {{ .Values.datasource.profile }} - banner: - charset: UTF-8 - jackson: - time-zone: UTC - date-format: "yyyy-MM-dd HH:mm:ss" - datasource: - driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://127.0.0.1:5432/dolphinscheduler - username: root - password: root - hikari: - connection-test-query: select 1 - pool-name: DolphinScheduler - quartz: - job-store-type: jdbc - jdbc: - initialize-schema: never - properties: - org.quartz.threadPool.threadPriority: 5 - org.quartz.jobStore.isClustered: true - org.quartz.jobStore.class: org.springframework.scheduling.quartz.LocalDataSourceJobStore - org.quartz.scheduler.instanceId: AUTO - org.quartz.jobStore.tablePrefix: QRTZ_ - org.quartz.jobStore.acquireTriggersWithinLock: true - org.quartz.scheduler.instanceName: DolphinScheduler - org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool - org.quartz.jobStore.useProperties: false - org.quartz.threadPool.makeThreadsDaemons: true - org.quartz.threadPool.threadCount: 25 - org.quartz.jobStore.misfireThreshold: 60000 - org.quartz.scheduler.batchTriggerAcquisitionMaxCount: 1 - org.quartz.scheduler.makeSchedulerThreadDaemon: true - org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate - org.quartz.jobStore.clusterCheckinInterval: 5000 - - # Mybatis-plus configuration, you don't need to change it - mybatis-plus: - mapper-locations: classpath:org/apache/dolphinscheduler/dao/mapper/*Mapper.xml - type-aliases-package: org.apache.dolphinscheduler.dao.entity - configuration: - cache-enabled: false - call-setters-on-nulls: true - map-underscore-to-camel-case: true - jdbc-type-for-null: NULL - global-config: - db-config: - id-type: auto - banner: false - - - registry: - type: zookeeper - zookeeper: - namespace: dolphinscheduler - connect-string: localhost:2181 - retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms - max-retries: 5 - session-timeout: 30s - connection-timeout: 9s - block-until-connected: 600ms - digest: ~ - - master: - listen-port: 5678 - # master fetch command num - fetch-command-num: 10 - # master prepare execute thread number to limit handle commands in parallel - pre-exec-threads: 10 - # master execute thread number to limit process instances in parallel - exec-threads: 100 - # master dispatch task number per batch, if all the tasks dispatch failed in a batch, will sleep 1s. - dispatch-task-number: 3 - # master host selector to select a suitable worker, default value: LowerWeight. Optional values include random, round_robin, lower_weight - host-selector: lower_weight - # master heartbeat interval - max-heartbeat-interval: 10s - # master commit task retry times - task-commit-retry-times: 5 - # master commit task interval - task-commit-interval: 1s - state-wheel-interval: 5s - server-load-protection: - # If set true, will open master overload protection - enabled: true - # Master max system cpu usage, when the master's system cpu usage is smaller then this value, master server can execute workflow. - max-system-cpu-usage-percentage-thresholds: 0.7 - # Master max jvm cpu usage, when the master's jvm cpu usage is smaller then this value, master server can execute workflow. - max-jvm-cpu-usage-percentage-thresholds: 0.7 - # Master max System memory usage , when the master's system memory usage is smaller then this value, master server can execute workflow. - max-system-memory-usage-percentage-thresholds: 0.7 - # Master max disk usage , when the master's disk usage is smaller then this value, master server can execute workflow. - max-disk-usage-percentage-thresholds: 0.7 - # failover interval, the unit is minute - failover-interval: 10m - # kill yarn / k8s application when failover taskInstance, default true - kill-application-when-task-failover: true - registry-disconnect-strategy: - # The disconnect strategy: stop, waiting - strategy: waiting - # The max waiting time to reconnect to registry if you set the strategy to waiting - max-waiting-time: 100s - worker-group-refresh-interval: 10s - - server: - port: 5679 - - management: - endpoints: - web: - exposure: - include: health,metrics,prometheus - endpoint: - health: - enabled: true - show-details: always - health: - db: - enabled: true - defaults: - enabled: false - metrics: - tags: - application: ${spring.application.name} - - metrics: - enabled: true - - # Override by profile - --- - spring: - config: - activate: - on-profile: mysql - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://127.0.0.1:3306/dolphinscheduler - username: root - password: root - quartz: - properties: - org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate -{{- end }} +{{- range $path, $config := .Values.master.customizedConfig }} + {{ $path }}: | +{{ $config | indent 4 -}} +{{- end -}} +{{- end -}} diff --git a/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-worker.yaml b/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-worker.yaml index b15cad5649..c1d81a1802 100644 --- a/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-worker.yaml +++ b/deploy/kubernetes/dolphinscheduler/templates/configmap-dolphinscheduler-worker.yaml @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -{{- if and .Values.worker.enabled }} +{{- if .Values.worker.enableCustomizedConfig }} apiVersion: v1 kind: ConfigMap metadata: @@ -23,86 +23,8 @@ metadata: app.kubernetes.io/name: {{ include "dolphinscheduler.fullname" . }}-worker {{- include "dolphinscheduler.worker.labels" . | nindent 4 }} data: - application.yaml: | - spring: - banner: - charset: UTF-8 - jackson: - time-zone: UTC - date-format: "yyyy-MM-dd HH:mm:ss" - autoconfigure: - exclude: - - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration - - registry: - type: zookeeper - zookeeper: - namespace: dolphinscheduler - connect-string: localhost:2181 - retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms - max-retries: 5 - session-timeout: 30s - connection-timeout: 9s - block-until-connected: 600ms - digest: ~ - - worker: - # worker listener port - listen-port: 1234 - # worker execute thread number to limit task instances in parallel - exec-threads: 100 - # worker heartbeat interval - max-heartbeat-interval: 10s - # worker host weight to dispatch tasks, default value 100 - host-weight: 100 - server-load-protection: - # If set true, will open worker overload protection - enabled: true - # Worker max system cpu usage, when the worker's system cpu usage is smaller then this value, worker server can be dispatched tasks. - max-system-cpu-usage-percentage-thresholds: 0.7 - # Worker max jvm cpu usage, when the worker's jvm cpu usage is smaller then this value, worker server can be dispatched tasks. - max-jvm-cpu-usage-percentage-thresholds: 0.7 - # Worker max System memory usage , when the master's system memory usage is smaller then this value, master server can execute workflow. - max-system-memory-usage-percentage-thresholds: 0.7 - # Worker max disk usage , when the worker's disk usage is smaller then this value, worker server can be dispatched tasks. - max-disk-usage-percentage-thresholds: 0.7 - registry-disconnect-strategy: - # The disconnect strategy: stop, waiting - strategy: waiting - # The max waiting time to reconnect to registry if you set the strategy to waiting - max-waiting-time: 100s - task-execute-threads-full-policy: REJECT - tenant-config: - # tenant corresponds to the user of the system, which is used by the worker to submit the job. If system does not have this user, it will be automatically created after the parameter worker.tenant.auto.create is true. - auto-create-tenant-enabled: true - # Scenes to be used for distributed users. For example, users created by FreeIpa are stored in LDAP. This parameter only applies to Linux, When this parameter is true, auto-create-tenant-enabled has no effect and will not automatically create tenants. - distributed-tenant-enabled: false - # If set true, will use worker bootstrap user as the tenant to execute task when the tenant is `default`. - default-tenant-enabled: false - - server: - port: 1235 - - management: - endpoints: - web: - exposure: - include: health,metrics,prometheus - endpoint: - health: - enabled: true - show-details: always - health: - db: - enabled: true - defaults: - enabled: false - metrics: - tags: - application: ${spring.application.name} - - metrics: - enabled: true -{{- end }} +{{- range $path, $config := .Values.worker.customizedConfig }} + {{ $path }}: | +{{ $config | indent 4 -}} +{{- end -}} +{{- end -}} diff --git a/deploy/kubernetes/dolphinscheduler/templates/deployment-dolphinscheduler-alert.yaml b/deploy/kubernetes/dolphinscheduler/templates/deployment-dolphinscheduler-alert.yaml index 6a5560b3d4..7a90d08025 100644 --- a/deploy/kubernetes/dolphinscheduler/templates/deployment-dolphinscheduler-alert.yaml +++ b/deploy/kubernetes/dolphinscheduler/templates/deployment-dolphinscheduler-alert.yaml @@ -115,9 +115,11 @@ spec: - name: config-volume mountPath: /opt/dolphinscheduler/conf/common.properties subPath: common.properties + {{- if .Values.alert.enableCustomizedConfig }} - name: alert-config-volume mountPath: /opt/dolphinscheduler/conf/application.yaml subPath: application.yaml + {{- end }} volumes: - name: {{ include "dolphinscheduler.fullname" . }}-alert {{- if .Values.alert.persistentVolumeClaim.enabled }} @@ -129,7 +131,9 @@ spec: - name: config-volume configMap: name: {{ include "dolphinscheduler.fullname" . }}-configs + {{- if .Values.alert.enableCustomizedConfig }} - name: alert-config-volume configMap: name: {{ include "dolphinscheduler.fullname" . }}-alert + {{- end }} {{- end }} diff --git a/deploy/kubernetes/dolphinscheduler/templates/deployment-dolphinscheduler-api.yaml b/deploy/kubernetes/dolphinscheduler/templates/deployment-dolphinscheduler-api.yaml index f36d2442ae..b4fa07256c 100644 --- a/deploy/kubernetes/dolphinscheduler/templates/deployment-dolphinscheduler-api.yaml +++ b/deploy/kubernetes/dolphinscheduler/templates/deployment-dolphinscheduler-api.yaml @@ -121,9 +121,11 @@ spec: mountPath: /opt/dolphinscheduler/conf/task-type-config.yaml subPath: task-type-config.yaml {{- end }} + {{- if .Values.api.enableCustomizedConfig }} - name: api-config-volume mountPath: /opt/dolphinscheduler/conf/application.yaml subPath: application.yaml + {{- end }} {{- include "dolphinscheduler.sharedStorage.volumeMount" . | nindent 12 }} {{- include "dolphinscheduler.fsFileResource.volumeMount" . | nindent 12 }} {{- include "dolphinscheduler.ldap.ssl.volumeMount" . | nindent 12 }} @@ -139,9 +141,11 @@ spec: - name: config-volume configMap: name: {{ include "dolphinscheduler.fullname" . }}-configs + {{- if .Values.api.enableCustomizedConfig }} - name: api-config-volume configMap: name: {{ include "dolphinscheduler.fullname" . }}-api + {{- end }} {{- include "dolphinscheduler.sharedStorage.volume" . | nindent 8 }} {{- include "dolphinscheduler.fsFileResource.volume" . | nindent 8 }} {{- include "dolphinscheduler.ldap.ssl.volume" . | nindent 8 }} diff --git a/deploy/kubernetes/dolphinscheduler/templates/statefulset-dolphinscheduler-master.yaml b/deploy/kubernetes/dolphinscheduler/templates/statefulset-dolphinscheduler-master.yaml index 1d9d517d0e..c4174b9ca0 100644 --- a/deploy/kubernetes/dolphinscheduler/templates/statefulset-dolphinscheduler-master.yaml +++ b/deploy/kubernetes/dolphinscheduler/templates/statefulset-dolphinscheduler-master.yaml @@ -109,9 +109,11 @@ spec: volumeMounts: - mountPath: "/opt/dolphinscheduler/logs" name: {{ include "dolphinscheduler.fullname" . }}-master + {{- if .Values.master.enableCustomizedConfig }} - name: master-config-volume mountPath: /opt/dolphinscheduler/conf/application.yaml subPath: application.yaml + {{- end }} {{- include "dolphinscheduler.sharedStorage.volumeMount" . | nindent 12 }} - name: config-volume mountPath: /opt/dolphinscheduler/conf/common.properties @@ -125,9 +127,11 @@ spec: {{- else }} emptyDir: {} {{- end }} + {{- if .Values.master.enableCustomizedConfig }} - name: master-config-volume configMap: name: {{ include "dolphinscheduler.fullname" . }}-master + {{- end }} {{- include "dolphinscheduler.sharedStorage.volume" . | nindent 8 }} - name: config-volume configMap: diff --git a/deploy/kubernetes/dolphinscheduler/templates/statefulset-dolphinscheduler-worker.yaml b/deploy/kubernetes/dolphinscheduler/templates/statefulset-dolphinscheduler-worker.yaml index 8d55be6f70..4c66ca5ffe 100644 --- a/deploy/kubernetes/dolphinscheduler/templates/statefulset-dolphinscheduler-worker.yaml +++ b/deploy/kubernetes/dolphinscheduler/templates/statefulset-dolphinscheduler-worker.yaml @@ -111,9 +111,11 @@ spec: name: {{ include "dolphinscheduler.fullname" . }}-worker-data - mountPath: "/opt/dolphinscheduler/logs" name: {{ include "dolphinscheduler.fullname" . }}-worker-logs + {{- if .Values.worker.enableCustomizedConfig }} - name: worker-config-volume mountPath: /opt/dolphinscheduler/conf/application.yaml subPath: application.yaml + {{- end }} - name: config-volume mountPath: /opt/dolphinscheduler/conf/common.properties subPath: common.properties @@ -142,9 +144,11 @@ spec: - name: {{ include "dolphinscheduler.fullname" . }}-worker-logs emptyDir: {} {{- end }} + {{- if .Values.worker.enableCustomizedConfig }} - name: worker-config-volume configMap: name: {{ include "dolphinscheduler.fullname" . }}-worker + {{- end }} - name: config-volume configMap: name: {{ include "dolphinscheduler.fullname" . }}-configs diff --git a/deploy/kubernetes/dolphinscheduler/values.yaml b/deploy/kubernetes/dolphinscheduler/values.yaml index e7e42c6d09..5d924d6bba 100644 --- a/deploy/kubernetes/dolphinscheduler/values.yaml +++ b/deploy/kubernetes/dolphinscheduler/values.yaml @@ -449,7 +449,19 @@ master: # requests: # memory: "2Gi" # cpu: "500m" - + # -- enable configure custom config + enableCustomizedConfig: false + # -- configure aligned with https://github.com/apache/dolphinscheduler/blob/dev/dolphinscheduler-master/src/main/resources/application.yaml + customizedConfig: { } + # customizedConfig: + # application.yaml: | + # profiles: + # active: postgresql + # banner: + # charset: UTF-8 + # jackson: + # time-zone: UTC + # date-format: "yyyy-MM-dd HH:mm:ss" # -- Periodic probe of container liveness. Container will be restarted if the probe fails. # More info: [container-probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) livenessProbe: @@ -578,6 +590,17 @@ worker: # -- Periodic probe of container liveness. Container will be restarted if the probe fails. # More info: [container-probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) + # -- enable configure custom config + enableCustomizedConfig: false + # -- configure aligned with https://github.com/apache/dolphinscheduler/blob/dev/dolphinscheduler-worker/src/main/resources/application.yaml + customizedConfig: { } +# customizedConfig: +# application.yaml: | +# banner: +# charset: UTF-8 +# jackson: +# time-zone: UTC +# date-format: "yyyy-MM-dd HH:mm:ss" livenessProbe: # -- Turn on and off liveness probe enabled: true @@ -740,7 +763,19 @@ alert: # requests: # memory: "1Gi" # cpu: "500m" - + # -- enable configure custom config + enableCustomizedConfig: false + # -- configure aligned with https://github.com/apache/dolphinscheduler/blob/dev/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/resources/application.yaml + customizedConfig: { } + # customizedConfig: + # application.yaml: | + # profiles: + # active: postgresql + # banner: + # charset: UTF-8 + # jackson: + # time-zone: UTC + # date-format: "yyyy-MM-dd HH:mm:ss" # -- Periodic probe of container liveness. Container will be restarted if the probe fails. # More info: [container-probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) livenessProbe: @@ -840,7 +875,19 @@ api: # requests: # memory: "1Gi" # cpu: "500m" - + # -- enable configure custom config + enableCustomizedConfig: false + # -- configure aligned with https://github.com/apache/dolphinscheduler/blob/dev/dolphinscheduler-api/src/main/resources/application.yaml + customizedConfig: { } + # customizedConfig: + # application.yaml: | + # profiles: + # active: postgresql + # banner: + # charset: UTF-8 + # jackson: + # time-zone: UTC + # date-format: "yyyy-MM-dd HH:mm:ss" # -- Periodic probe of container liveness. Container will be restarted if the probe fails. # More info: [container-probes](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes) livenessProbe: diff --git a/docs/docs/en/contribute/join/pull-request.md b/docs/docs/en/contribute/join/pull-request.md index ad954a7c6e..10b51ba585 100644 --- a/docs/docs/en/contribute/join/pull-request.md +++ b/docs/docs/en/contribute/join/pull-request.md @@ -31,7 +31,7 @@ The corresponding relationship between `Pull Request Type` and `Issue Type` is a Bug Fix - [Fix-3333][server] Fix xxx + [Fix-3333][ui] Fix xxx Improvement @@ -44,9 +44,24 @@ The corresponding relationship between `Pull Request Type` and `Issue Type` is a [Test-3333][api] Add the e2e test of xxx - Sub-Task - (Parent type corresponding to Sub-Task) - [Feature-3333][server] Implement xxx + Doc + Doc + [Doc-3333] Improve xxx + + + E2E + E2E + [E2E-3333] Implement xxx + + + CI + CI + [CI] Improve xxx + + + Chore + Chore + [Chore] Improve xxx diff --git a/docs/docs/en/guide/installation/cluster.md b/docs/docs/en/guide/installation/cluster.md index 0014a9c7f1..ce5b60baa2 100644 --- a/docs/docs/en/guide/installation/cluster.md +++ b/docs/docs/en/guide/installation/cluster.md @@ -14,21 +14,7 @@ Configure all the configurations refer to [pseudo-cluster deployment](pseudo-clu ### Modify Configuration -This step differs quite a lot from [pseudo-cluster deployment](pseudo-cluster.md), because the deployment script transfers the required resources for installation to each deployment machine by using `scp`. So we only need to modify the configuration of the machine that runs `install.sh` script and configurations will dispatch to cluster by `scp`. The configuration file is under the path `bin/env/install_env.sh`, here we only need to modify section **INSTALL MACHINE**, **DolphinScheduler ENV, Database, Registry Server** and keep other sections the same as [pseudo-cluster deployment](pseudo-cluster.md), the following describes the parameters that must be modified: - -```shell -# --------------------------------------------------------- -# INSTALL MACHINE -# --------------------------------------------------------- -# Using IP or machine hostname for the server going to deploy master, worker, API server, the IP of the server -# If you using a hostname, make sure machines could connect each other by hostname -# As below, the hostname of the machine deploying DolphinScheduler is ds1, ds2, ds3, ds4, ds5, where ds1, ds2 install the master server, ds3, ds4, and ds5 installs worker server, the alert server is installed in ds4, and the API server is installed in ds5 -ips="ds1,ds2,ds3,ds4,ds5" -masters="ds1,ds2" -workers="ds3:default,ds4:default,ds5:default" -alertServer="ds4" -apiServers="ds5" -``` +This step differs quite a lot from [pseudo-cluster deployment](pseudo-cluster.md), please use `scp` or other methods to distribute the configuration files to each machine, then modify the configuration files. ## Start and Login DolphinScheduler diff --git a/docs/docs/en/guide/installation/pseudo-cluster.md b/docs/docs/en/guide/installation/pseudo-cluster.md index 64f28022e4..f0dc100d84 100644 --- a/docs/docs/en/guide/installation/pseudo-cluster.md +++ b/docs/docs/en/guide/installation/pseudo-cluster.md @@ -71,31 +71,7 @@ Go to the ZooKeeper installation directory, copy configure file `zoo_sample.cfg` ## Modify Configuration After completing the preparation of the basic environment, you need to modify the configuration file according to the -environment you used. Change the environment configurations via `export =`. The configuration files are located in directory `bin/env` as `install_env.sh` and `dolphinscheduler_env.sh`. - -### Modify `install_env.sh` - -File `install_env.sh` describes which machines will be installed DolphinScheduler and what server will be installed on -each machine. You could find this file in the path `bin/env/install_env.sh` and the detail of the configuration as below. - -```shell -# --------------------------------------------------------- -# INSTALL MACHINE -# --------------------------------------------------------- -# Due to the master, worker, and API server being deployed on a single node, the IP of the server is the machine IP or localhost -ips="localhost" -sshPort="22" -masters="localhost" -workers="localhost:default" -alertServer="localhost" -apiServers="localhost" - -# DolphinScheduler installation path, it will auto-create if not exists -installPath=~/dolphinscheduler - -# Deploy user, use the user you create in section **Configure machine SSH password-free login** -deployUser="dolphinscheduler" -``` +environment you used. Change the environment configurations via `export =`. The configuration files are located in directory `bin/env` as `dolphinscheduler_env.sh`. ### Modify `dolphinscheduler_env.sh` @@ -146,11 +122,7 @@ Follow the instructions in [datasource-setting](../howto/datasource-setting.md) ## Start DolphinScheduler -Use **deployment user** you created above, running the following command to complete the deployment, and the server log will be stored in the logs folder. - -```shell -bash ./bin/install.sh -``` +Use **deployment user** you created above, running the command to complete the deployment, and the server log will be stored in the logs folder. > **_Note:_** For the first time deployment, there maybe occur five times of `sh: bin/dolphinscheduler-daemon.sh: No such file or directory` in the terminal, > this is non-important information that you can ignore. diff --git a/docs/docs/en/guide/metrics/metrics.md b/docs/docs/en/guide/metrics/metrics.md index 3470340465..9ba7cc73e7 100644 --- a/docs/docs/en/guide/metrics/metrics.md +++ b/docs/docs/en/guide/metrics/metrics.md @@ -91,6 +91,11 @@ For example, you can get the master metrics by `curl http://localhost:5679/actua - stop: the number of stopped workflow instances - failover: the number of workflow instance fail-overs +### RPC Related Metrics + +- ds.rpc.client.sync.request.exception.count: (counter) the number of exceptions occurred in sync rpc requests +- ds.rpc.client.sync.request.duration.time: (histogram) the time cost of sync rpc requests + ### Master Server Metrics - ds.master.overload.count: (counter) the number of times the master overloaded diff --git a/docs/docs/zh/contribute/join/pull-request.md b/docs/docs/zh/contribute/join/pull-request.md index e147a0a200..3ee6caf75e 100644 --- a/docs/docs/zh/contribute/join/pull-request.md +++ b/docs/docs/zh/contribute/join/pull-request.md @@ -33,7 +33,7 @@ waste time。 Bug Fix - [Fix-3333][server] Fix xxx + [Fix-3333][ui] Fix xxx Improvement @@ -46,9 +46,23 @@ waste time。 [Test-3333][api] Add the e2e test of xxx - Sub-Task - Sub-Task 对应的父类型 - [Feature-3333][server] Implement xxx + Doc + Doc + [Doc-3333] Improve xxx + + E2E + E2E + [E2E-3333] Implement xxx + + + CI + CI + [CI] Improve xxx + + + Chore + Chore + [Chore] Improve xxx diff --git a/docs/docs/zh/guide/installation/cluster.md b/docs/docs/zh/guide/installation/cluster.md index 97c1792787..c266a671e0 100644 --- a/docs/docs/zh/guide/installation/cluster.md +++ b/docs/docs/zh/guide/installation/cluster.md @@ -14,21 +14,7 @@ ### 修改相关配置 -这个是与[伪集群部署](pseudo-cluster.md)差异较大的一步,因为部署脚本会通过 `scp` 的方式将安装需要的资源传输到各个机器上,所以这一步我们仅需要修改运行`install.sh`脚本的所在机器的配置即可。配置文件在路径在`bin/env/install_env.sh`下,此处我们仅需修改**INSTALL MACHINE**,**DolphinScheduler ENV、Database、Registry Server**与伪集群部署保持一致,下面对必须修改参数进行说明 - -```shell -# --------------------------------------------------------- -# INSTALL MACHINE -# --------------------------------------------------------- -# 需要配置master、worker、API server,所在服务器的IP均为机器IP或者localhost -# 如果是配置hostname的话,需要保证机器间可以通过hostname相互链接 -# 如下图所示,部署 DolphinScheduler 机器的 hostname 为 ds1,ds2,ds3,ds4,ds5,其中 ds1,ds2 安装 master 服务,ds3,ds4,ds5安装 worker 服务,alert server安装在ds4中,api server 安装在ds5中 -ips="ds1,ds2,ds3,ds4,ds5" -masters="ds1,ds2" -workers="ds3:default,ds4:default,ds5:default" -alertServer="ds4" -apiServers="ds5" -``` +这个是与[伪集群部署](pseudo-cluster.md)差异较大的一步,请使用 scp 等方式将配置文件分发到各台机器上,然后修改配置文件 ## 启动 DolphinScheduler && 登录 DolphinScheduler && 启停服务 diff --git a/docs/docs/zh/guide/installation/pseudo-cluster.md b/docs/docs/zh/guide/installation/pseudo-cluster.md index a199167e04..8e1c133c07 100644 --- a/docs/docs/zh/guide/installation/pseudo-cluster.md +++ b/docs/docs/zh/guide/installation/pseudo-cluster.md @@ -70,30 +70,7 @@ chmod 600 ~/.ssh/authorized_keys ## 修改相关配置 -完成基础环境的准备后,需要根据你的机器环境修改配置文件。配置文件可以在目录 `bin/env` 中找到,他们分别是 并命名为 `install_env.sh` 和 `dolphinscheduler_env.sh`。 - -### 修改 `install_env.sh` 文件 - -文件 `install_env.sh` 描述了哪些机器将被安装 DolphinScheduler 以及每台机器对应安装哪些服务。您可以在路径 `bin/env/install_env.sh` 中找到此文件,可通过以下方式更改 env 变量,export =,配置详情如下。 - -```shell -# --------------------------------------------------------- -# INSTALL MACHINE -# --------------------------------------------------------- -# Due to the master, worker, and API server being deployed on a single node, the IP of the server is the machine IP or localhost -ips="localhost" -sshPort="22" -masters="localhost" -workers="localhost:default" -alertServer="localhost" -apiServers="localhost" - -# DolphinScheduler installation path, it will auto-create if not exists -installPath=~/dolphinscheduler - -# Deploy user, use the user you create in section **Configure machine SSH password-free login** -deployUser="dolphinscheduler" -``` +完成基础环境的准备后,需要根据你的机器环境修改配置文件。配置文件可以在目录 `bin/env/dolphinscheduler_env.sh` 中找到。 ### 修改 `dolphinscheduler_env.sh` 文件 @@ -141,11 +118,7 @@ export PATH=$HADOOP_HOME/bin:$SPARK_HOME/bin:$PYTHON_LAUNCHER:$JAVA_HOME/bin:$HI ## 启动 DolphinScheduler -使用上面创建的**部署用户**运行以下命令完成部署,部署后的运行日志将存放在 logs 文件夹内 - -```shell -bash ./bin/install.sh -``` +使用上面创建的**部署用户**运行命令完成部署,部署后的运行日志将存放在 logs 文件夹内 > **_注意:_** 第一次部署的话,可能出现 5 次`sh: bin/dolphinscheduler-daemon.sh: No such file or directory`相关信息,此为非重要信息直接忽略即可 diff --git a/docs/docs/zh/guide/metrics/metrics.md b/docs/docs/zh/guide/metrics/metrics.md index aa620b873e..4865a14222 100644 --- a/docs/docs/zh/guide/metrics/metrics.md +++ b/docs/docs/zh/guide/metrics/metrics.md @@ -91,6 +91,11 @@ metrics exporter端口`server.port`是在application.yaml里定义的: master: ` - stop:停止的工作流实例数量 - failover:容错的工作流实例数量 +### RPC相关指标 + +- ds.rpc.client.sync.request.exception.count: (counter) 同步rpc请求异常数 +- ds.rpc.client.sync.request.duration.time: (histogram) 同步rpc请求耗时 + ### Master Server指标 - ds.master.overload.count: (counter) master过载次数 diff --git a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/resources/application.yaml b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/resources/application.yaml index 6fbcc04feb..927cbc3c2c 100644 --- a/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/resources/application.yaml +++ b/dolphinscheduler-alert/dolphinscheduler-alert-server/src/main/resources/application.yaml @@ -82,12 +82,12 @@ registry: namespace: dolphinscheduler connect-string: localhost:2181 retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms + base-sleep-time: 1s + max-sleep: 3s max-retries: 5 - session-timeout: 30s - connection-timeout: 9s - block-until-connected: 600ms + session-timeout: 60s + connection-timeout: 15s + block-until-connected: 15s digest: ~ metrics: diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/ApiApplicationServer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/ApiApplicationServer.java index f8b705179a..6b183e369c 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/ApiApplicationServer.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/ApiApplicationServer.java @@ -19,22 +19,14 @@ package org.apache.dolphinscheduler.api; import org.apache.dolphinscheduler.api.metrics.ApiServerMetrics; import org.apache.dolphinscheduler.common.CommonConfiguration; -import org.apache.dolphinscheduler.common.enums.PluginType; import org.apache.dolphinscheduler.common.thread.DefaultUncaughtExceptionHandler; import org.apache.dolphinscheduler.dao.DaoConfiguration; import org.apache.dolphinscheduler.dao.PluginDao; -import org.apache.dolphinscheduler.dao.entity.PluginDefine; import org.apache.dolphinscheduler.plugin.datasource.api.plugin.DataSourceProcessorProvider; import org.apache.dolphinscheduler.plugin.storage.api.StorageConfiguration; -import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; import org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager; import org.apache.dolphinscheduler.registry.api.RegistryConfiguration; import org.apache.dolphinscheduler.service.ServiceConfiguration; -import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; -import java.util.Map; import lombok.extern.slf4j.Slf4j; @@ -68,17 +60,7 @@ public class ApiApplicationServer { @EventListener public void run(ApplicationReadyEvent readyEvent) { log.info("Received spring application context ready event will load taskPlugin and write to DB"); - // install task plugin - TaskPluginManager.loadPlugin(); DataSourceProcessorProvider.initialize(); - for (Map.Entry entry : TaskPluginManager.getTaskChannelFactoryMap().entrySet()) { - String taskPluginName = entry.getKey(); - TaskChannelFactory taskChannelFactory = entry.getValue(); - List params = taskChannelFactory.getParams(); - String paramsJson = PluginParamsTransfer.transferParamsToJson(params); - - PluginDefine pluginDefine = new PluginDefine(taskPluginName, PluginType.TASK.getDesc(), paramsJson); - pluginDao.addOrUpdatePluginDefine(pluginDefine); - } + TaskPluginManager.loadTaskPlugin(); } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ResourcesController.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ResourcesController.java index 0338453197..620993d61b 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ResourcesController.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/ResourcesController.java @@ -28,42 +28,50 @@ import static org.apache.dolphinscheduler.api.enums.Status.QUERY_DATASOURCE_BY_T import static org.apache.dolphinscheduler.api.enums.Status.QUERY_RESOURCES_LIST_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.QUERY_RESOURCES_LIST_PAGING; import static org.apache.dolphinscheduler.api.enums.Status.QUERY_UDF_FUNCTION_LIST_PAGING_ERROR; -import static org.apache.dolphinscheduler.api.enums.Status.RESOURCE_FILE_IS_EMPTY; import static org.apache.dolphinscheduler.api.enums.Status.RESOURCE_NOT_EXIST; import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_RESOURCE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.UPDATE_UDF_FUNCTION_ERROR; -import static org.apache.dolphinscheduler.api.enums.Status.VERIFY_RESOURCE_BY_NAME_AND_TYPE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.VERIFY_UDF_FUNCTION_NAME_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.VIEW_RESOURCE_FILE_ON_LINE_ERROR; import static org.apache.dolphinscheduler.api.enums.Status.VIEW_UDF_FUNCTION_ERROR; import org.apache.dolphinscheduler.api.audit.OperatorLog; import org.apache.dolphinscheduler.api.audit.enums.AuditType; -import org.apache.dolphinscheduler.api.dto.resources.DeleteDataTransferResponse; +import org.apache.dolphinscheduler.api.dto.resources.CreateDirectoryRequest; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileFromContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.DeleteResourceRequest; +import org.apache.dolphinscheduler.api.dto.resources.DownloadFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.FetchFileContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.PagingResourceItemRequest; +import org.apache.dolphinscheduler.api.dto.resources.RenameDirectoryRequest; +import org.apache.dolphinscheduler.api.dto.resources.RenameFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.ResourceComponent; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileFromContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileRequest; import org.apache.dolphinscheduler.api.exceptions.ApiException; import org.apache.dolphinscheduler.api.service.ResourcesService; import org.apache.dolphinscheduler.api.service.UdfFuncService; import org.apache.dolphinscheduler.api.utils.PageInfo; import org.apache.dolphinscheduler.api.utils.Result; +import org.apache.dolphinscheduler.api.vo.ResourceItemVO; +import org.apache.dolphinscheduler.api.vo.resources.FetchFileContentResponse; import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ProgramType; import org.apache.dolphinscheduler.common.enums.UdfType; import org.apache.dolphinscheduler.dao.entity.User; -import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; import org.apache.dolphinscheduler.plugin.task.api.utils.ParameterUtils; import org.apache.dolphinscheduler.spi.enums.ResourceType; import org.apache.commons.lang3.StringUtils; -import java.util.Map; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -77,15 +85,14 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import com.google.common.io.Files; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; -/** - * resources controller - */ @Tag(name = "RESOURCES_TAG") @RestController @RequestMapping("resources") @@ -94,41 +101,34 @@ public class ResourcesController extends BaseController { @Autowired private ResourcesService resourceService; + @Autowired private UdfFuncService udfFuncService; - /** - * @param loginUser login user - * @param type type - * @param alias alias - * @param pid parent id - * @param currentDir current directory - * @return create result code - */ @Operation(summary = "createDirectory", description = "CREATE_RESOURCE_NOTES") @Parameters({ @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), @Parameter(name = "name", description = "RESOURCE_NAME", required = true, schema = @Schema(implementation = String.class)), - @Parameter(name = "pid", description = "RESOURCE_PID", required = true, schema = @Schema(implementation = int.class, example = "10")), @Parameter(name = "currentDir", description = "RESOURCE_CURRENT_DIR", required = true, schema = @Schema(implementation = String.class))}) @PostMapping(value = "/directory") @ApiException(CREATE_RESOURCE_ERROR) @OperatorLog(auditType = AuditType.FOLDER_CREATE) - public Result createDirectory(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "type") ResourceType type, - @RequestParam(value = "name") String alias, - @RequestParam(value = "pid") int pid, - @RequestParam(value = "currentDir") String currentDir) { - // todo verify the directory name - return resourceService.createDirectory(loginUser, alias, type, pid, currentDir); + public Result createDirectory(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "type") ResourceType type, + @RequestParam(value = "name") String directoryName, + @RequestParam(value = "currentDir") String parentDirectory) { + + CreateDirectoryRequest createDirectoryRequest = CreateDirectoryRequest.builder() + .loginUser(loginUser) + .directoryName(directoryName) + .type(type) + .parentAbsoluteDirectory(parentDirectory) + .build(); + resourceService.createDirectory(createDirectoryRequest); + return Result.success(null); } - /** - * create resource - * - * @return create result code - */ - @Operation(summary = "createResource", description = "CREATE_RESOURCE_NOTES") + @Operation(summary = "uploadFile", description = "CREATE_FILE") @Parameters({ @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), @Parameter(name = "name", description = "RESOURCE_NAME", required = true, schema = @Schema(implementation = String.class)), @@ -137,224 +137,24 @@ public class ResourcesController extends BaseController { @PostMapping() @ApiException(CREATE_RESOURCE_ERROR) @OperatorLog(auditType = AuditType.FILE_CREATE) - public Result createResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "type") ResourceType type, - @RequestParam(value = "name") String alias, - @RequestParam("file") MultipartFile file, - @RequestParam(value = "currentDir") String currentDir) { - // todo verify the file name - return resourceService.uploadResource(loginUser, alias, type, file, currentDir); + public Result createFile(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "type") ResourceType type, + @RequestParam(value = "name") String fileName, + @RequestParam("file") MultipartFile file, + @RequestParam(value = "currentDir") String parentDirectoryAbsolutePath) { + + CreateFileRequest uploadFileRequest = CreateFileRequest.builder() + .loginUser(loginUser) + .fileName(fileName) + .file(file) + .type(type) + .parentAbsoluteDirectory(parentDirectoryAbsolutePath) + .build(); + resourceService.createFile(uploadFileRequest); + return Result.success(); } - /** - * update resource - * - * @param loginUser login user - * @param alias alias - * @param type resource type - * @param file resource file - * @return update result code - */ - @Operation(summary = "updateResource", description = "UPDATE_RESOURCE_NOTES") - @Parameters({ - @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class)), - @Parameter(name = "tenantCode", description = "TENANT_CODE", required = true, schema = @Schema(implementation = String.class)), - @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), - @Parameter(name = "name", description = "RESOURCE_NAME", required = true, schema = @Schema(implementation = String.class)), - @Parameter(name = "file", description = "RESOURCE_FILE", required = true, schema = @Schema(implementation = MultipartFile.class))}) - @PutMapping() - @ApiException(UPDATE_RESOURCE_ERROR) - @OperatorLog(auditType = AuditType.FILE_UPDATE) - public Result updateResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "fullName") String fullName, - @RequestParam(value = "tenantCode", required = false) String tenantCode, - @RequestParam(value = "type") ResourceType type, - @RequestParam(value = "name") String alias, - @RequestParam(value = "file", required = false) MultipartFile file) { - return resourceService.updateResource(loginUser, fullName, tenantCode, alias, type, file); - } - - /** - * query resources list - * - * @param loginUser login user - * @param type resource type - * @return resource list - */ - @Operation(summary = "queryResourceList", description = "QUERY_RESOURCE_LIST_NOTES") - @Parameters({ - @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), - @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class))}) - @GetMapping(value = "/list") - @ResponseStatus(HttpStatus.OK) - @ApiException(QUERY_RESOURCES_LIST_ERROR) - public Result queryResourceList(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "type") ResourceType type, - @RequestParam(value = "fullName") String fullName) { - Map result = resourceService.queryResourceList(loginUser, type, fullName); - return returnDataList(result); - } - - /** - * query resources list paging - * - * @param loginUser login user - * @param type resource type - * @param searchVal search value - * @param pageNo page number - * @param pageSize page size - * @return resource list page - */ - @Operation(summary = "queryResourceListPaging", description = "QUERY_RESOURCE_LIST_PAGING_NOTES") - @Parameters({ - @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), - @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class, example = "bucket_name/tenant_name/type/ds")), - @Parameter(name = "searchVal", description = "SEARCH_VAL", schema = @Schema(implementation = String.class)), - @Parameter(name = "pageNo", description = "PAGE_NO", required = true, schema = @Schema(implementation = int.class, example = "1")), - @Parameter(name = "pageSize", description = "PAGE_SIZE", required = true, schema = @Schema(implementation = int.class, example = "20"))}) - @GetMapping() - @ResponseStatus(HttpStatus.OK) - @ApiException(QUERY_RESOURCES_LIST_PAGING) - public Result> queryResourceListPaging(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "fullName") String fullName, - @RequestParam(value = "tenantCode") String tenantCode, - @RequestParam(value = "type") ResourceType type, - @RequestParam("pageNo") Integer pageNo, - @RequestParam(value = "searchVal", required = false) String searchVal, - @RequestParam("pageSize") Integer pageSize) { - checkPageParams(pageNo, pageSize); - - searchVal = ParameterUtils.handleEscapes(searchVal); - return resourceService.queryResourceListPaging(loginUser, fullName, tenantCode, type, searchVal, pageNo, - pageSize); - } - - /** - * delete resource - * - * @param loginUser login user - * @return delete result code - */ - @Operation(summary = "deleteResource", description = "DELETE_RESOURCE_BY_ID_NOTES") - @Parameters({ - @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class, example = "test/"))}) - @DeleteMapping() - @ResponseStatus(HttpStatus.OK) - @ApiException(DELETE_RESOURCE_ERROR) - @OperatorLog(auditType = AuditType.FILE_DELETE) - public Result deleteResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "fullName") String fullName, - @RequestParam(value = "tenantCode", required = false) String tenantCode) throws Exception { - return resourceService.delete(loginUser, fullName, tenantCode); - } - - /** - * delete DATA_TRANSFER data - * - * @param loginUser login user - * @return delete result code - */ - @Operation(summary = "deleteDataTransferData", description = "Delete the N days ago data of DATA_TRANSFER ") - @Parameters({ - @Parameter(name = "days", description = "N days ago", required = true, schema = @Schema(implementation = Integer.class))}) - @DeleteMapping(value = "/data-transfer") - @ResponseStatus(HttpStatus.OK) - @ApiException(DELETE_RESOURCE_ERROR) - public DeleteDataTransferResponse deleteDataTransferData(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "days") Integer days) { - return resourceService.deleteDataTransferData(loginUser, days); - } - - /** - * verify resource by alias and type - * - * @param loginUser login user - * @param fullName resource full name - * @param type resource type - * @return true if the resource name not exists, otherwise return false - */ - @Operation(summary = "verifyResourceName", description = "VERIFY_RESOURCE_NAME_NOTES") - @Parameters({ - @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), - @Parameter(name = "fullName", description = "RESOURCE_FULL_NAME", required = true, schema = @Schema(implementation = String.class))}) - @GetMapping(value = "/verify-name") - @ResponseStatus(HttpStatus.OK) - @ApiException(VERIFY_RESOURCE_BY_NAME_AND_TYPE_ERROR) - public Result verifyResourceName(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "fullName") String fullName, - @RequestParam(value = "type") ResourceType type) { - return resourceService.verifyResourceName(fullName, type, loginUser); - } - - /** - * query resources by type - * - * @param loginUser login user - * @param type resource type - * @return resource list - */ - @Operation(summary = "queryResourceByProgramType", description = "QUERY_RESOURCE_LIST_NOTES") - @Parameters({ - @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class))}) - @GetMapping(value = "/query-by-type") - @ResponseStatus(HttpStatus.OK) - @ApiException(QUERY_RESOURCES_LIST_ERROR) - public Result queryResourceJarList(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "type") ResourceType type, - @RequestParam(value = "programType", required = false) ProgramType programType) { - return resourceService.queryResourceByProgramType(loginUser, type, programType); - } - - /** - * query resource by file name and type - * - * @param loginUser login user - * @param fileName resource full name - * @param tenantCode tenantCode of the owner of the resource - * @param type resource type - * @return true if the resource name not exists, otherwise return false - */ - @Operation(summary = "queryResourceByFileName", description = "QUERY_BY_RESOURCE_FILE_NAME") - @Parameters({ - @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), - @Parameter(name = "fileName", description = "RESOURCE_FILE_NAME", required = true, schema = @Schema(implementation = String.class)), - @Parameter(name = "tenantCode", description = "TENANT_CODE", required = true, schema = @Schema(implementation = String.class)),}) - @GetMapping(value = "/query-file-name") - @ResponseStatus(HttpStatus.OK) - @ApiException(RESOURCE_NOT_EXIST) - public Result queryResourceByFileName(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "fileName", required = false) String fileName, - @RequestParam(value = "tenantCode", required = false) String tenantCode, - @RequestParam(value = "type") ResourceType type) { - - return resourceService.queryResourceByFileName(loginUser, fileName, type, tenantCode); - } - - /** - * view resource file online - * - * @param loginUser login user - * @param skipLineNum skip line number - * @param limit limit - * @return resource content - */ - @Operation(summary = "viewResource", description = "VIEW_RESOURCE_BY_ID_NOTES") - @Parameters({ - @Parameter(name = "fullName", description = "RESOURCE_FULL_NAME", required = true, schema = @Schema(implementation = String.class, example = "tenant/1.png")), - @Parameter(name = "tenantCode", description = "TENANT_CODE", required = true, schema = @Schema(implementation = String.class)), - @Parameter(name = "skipLineNum", description = "SKIP_LINE_NUM", required = true, schema = @Schema(implementation = int.class, example = "100")), - @Parameter(name = "limit", description = "LIMIT", required = true, schema = @Schema(implementation = int.class, example = "100"))}) - @GetMapping(value = "/view") - @ApiException(VIEW_RESOURCE_FILE_ON_LINE_ERROR) - public Result viewResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "skipLineNum") int skipLineNum, - @RequestParam(value = "limit") int limit, - @RequestParam(value = "fullName") String fullName, - @RequestParam(value = "tenantCode") String tenantCode) { - return resourceService.readResource(loginUser, fullName, tenantCode, skipLineNum, limit); - } - - @Operation(summary = "onlineCreateResource", description = "ONLINE_CREATE_RESOURCE_NOTES") + @Operation(summary = "createFileFromContent", description = "ONLINE_CREATE_RESOURCE_NOTES") @Parameters({ @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), @Parameter(name = "fileName", description = "RESOURCE_NAME", required = true, schema = @Schema(implementation = String.class)), @@ -365,27 +165,24 @@ public class ResourcesController extends BaseController { @PostMapping(value = "/online-create") @ApiException(CREATE_RESOURCE_FILE_ON_LINE_ERROR) @OperatorLog(auditType = AuditType.FILE_CREATE) - public Result createResourceFile(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "type") ResourceType type, - @RequestParam(value = "fileName") String fileName, - @RequestParam(value = "suffix") String fileSuffix, - @RequestParam(value = "content") String content, - @RequestParam(value = "currentDir") String currentDir) { - if (StringUtils.isEmpty(content)) { - log.error("resource file contents are not allowed to be empty"); - return error(RESOURCE_FILE_IS_EMPTY.getCode(), RESOURCE_FILE_IS_EMPTY.getMsg()); - } - return resourceService.createResourceFile(loginUser, type, fileName, fileSuffix, content, currentDir); + public Result createFileFromContent(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "type") ResourceType type, + @RequestParam(value = "fileName") String fileName, + @RequestParam(value = "suffix") String fileSuffix, + @RequestParam(value = "content") String fileContent, + @RequestParam(value = "currentDir") String fileParentDirectoryAbsolutePath) { + CreateFileFromContentRequest createFileFromContentRequest = CreateFileFromContentRequest.builder() + .loginUser(loginUser) + .fileName(fileName + "." + fileSuffix) + .fileContent(fileContent) + .type(type) + .parentAbsoluteDirectory(fileParentDirectoryAbsolutePath) + .build(); + resourceService.createFileFromContent(createFileFromContentRequest); + return Result.success(); } - /** - * edit resource file online - * - * @param loginUser login user - * @param content content - * @return update result code - */ - @Operation(summary = "updateResourceContent", description = "UPDATE_RESOURCE_NOTES") + @Operation(summary = "updateFileContent", description = "UPDATE_RESOURCE_NOTES") @Parameters({ @Parameter(name = "content", description = "CONTENT", required = true, schema = @Schema(implementation = String.class)), @Parameter(name = "fullName", description = "FULL_NAME", required = true, schema = @Schema(implementation = String.class)), @@ -393,52 +190,170 @@ public class ResourcesController extends BaseController { @PutMapping(value = "/update-content") @ApiException(EDIT_RESOURCE_FILE_ON_LINE_ERROR) @OperatorLog(auditType = AuditType.FILE_UPDATE) - public Result updateResourceContent(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "fullName") String fullName, - @RequestParam(value = "tenantCode") String tenantCode, - @RequestParam(value = "content") String content) { - if (StringUtils.isEmpty(content)) { - log.error("The resource file contents are not allowed to be empty"); - return error(RESOURCE_FILE_IS_EMPTY.getCode(), RESOURCE_FILE_IS_EMPTY.getMsg()); - } - return resourceService.updateResourceContent(loginUser, fullName, tenantCode, content); + public Result updateFileContent(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "fullName") String fileAbsolutePath, + @RequestParam(value = "content") String fileContent) { + UpdateFileFromContentRequest updateFileContentRequest = UpdateFileFromContentRequest.builder() + .loginUser(loginUser) + .fileContent(fileContent) + .fileAbsolutePath(fileAbsolutePath) + .build(); + resourceService.updateFileFromContent(updateFileContentRequest); + return Result.success(); + } + + @Operation(summary = "updateResource", description = "UPDATE_RESOURCE_NOTES") + @Parameters({ + @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class)), + @Parameter(name = "tenantCode", description = "TENANT_CODE", required = true, schema = @Schema(implementation = String.class)), + @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), + @Parameter(name = "name", description = "RESOURCE_NAME", required = true, schema = @Schema(implementation = String.class)), + @Parameter(name = "file", description = "RESOURCE_FILE", required = true, schema = @Schema(implementation = MultipartFile.class))}) + @PutMapping() + @ApiException(UPDATE_RESOURCE_ERROR) + @OperatorLog(auditType = AuditType.FILE_UPDATE) + public Result updateResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "fullName") String resourceAbsolutePath, + @RequestParam(value = "name") String resourceName, + @RequestParam(value = "file", required = false) MultipartFile file) { + if (StringUtils.isEmpty(Files.getFileExtension(resourceName))) { + RenameDirectoryRequest renameDirectoryRequest = RenameDirectoryRequest.builder() + .loginUser(loginUser) + .directoryAbsolutePath(resourceAbsolutePath) + .newDirectoryName(resourceName) + .build(); + resourceService.renameDirectory(renameDirectoryRequest); + return Result.success(); + } + + if (file == null) { + RenameFileRequest renameFileRequest = RenameFileRequest.builder() + .loginUser(loginUser) + .fileAbsolutePath(resourceAbsolutePath) + .newFileName(resourceName) + .build(); + resourceService.renameFile(renameFileRequest); + return Result.success(); + } + UpdateFileRequest updateFileRequest = UpdateFileRequest.builder() + .loginUser(loginUser) + .fileAbsolutePath(resourceAbsolutePath) + .file(file) + .build(); + resourceService.updateFile(updateFileRequest); + return Result.success(); + } + + @Operation(summary = "pagingResourceItemRequest", description = "PAGING_RESOURCE_ITEM_LIST") + @Parameters({ + @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), + @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class, example = "bucket_name/tenant_name/type/ds")), + @Parameter(name = "searchVal", description = "SEARCH_VAL", schema = @Schema(implementation = String.class)), + @Parameter(name = "pageNo", description = "PAGE_NO", required = true, schema = @Schema(implementation = int.class, example = "1")), + @Parameter(name = "pageSize", description = "PAGE_SIZE", required = true, schema = @Schema(implementation = int.class, example = "20"))}) + @GetMapping() + @ResponseStatus(HttpStatus.OK) + @ApiException(QUERY_RESOURCES_LIST_PAGING) + public Result> pagingResourceItemRequest(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "fullName") String resourceAbsolutePath, + @RequestParam(value = "type") ResourceType resourceType, + @RequestParam(value = "searchVal", required = false) String resourceNameKeyWord, + @RequestParam("pageNo") Integer pageNo, + @RequestParam("pageSize") Integer pageSize) { + PagingResourceItemRequest pagingResourceItemRequest = PagingResourceItemRequest.builder() + .loginUser(loginUser) + .resourceAbsolutePath(resourceAbsolutePath) + .resourceType(resourceType) + .resourceNameKeyWord(StringUtils.trim(ParameterUtils.handleEscapes(resourceNameKeyWord))) + .pageNo(pageNo) + .pageSize(pageSize) + .build(); + pagingResourceItemRequest.checkPageNoAndPageSize(); + + return Result.success(resourceService.pagingResourceItem(pagingResourceItemRequest)); + } + + // todo: this api is used for udf, we should remove it + @Operation(summary = "queryResourceList", description = "QUERY_RESOURCE_LIST_NOTES") + @Parameters({ + @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class)), + @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class))}) + @GetMapping(value = "/list") + @ResponseStatus(HttpStatus.OK) + @ApiException(QUERY_RESOURCES_LIST_ERROR) + public Result> queryResourceList(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "type") ResourceType type) { + return Result.success(resourceService.queryResourceFiles(loginUser, type)); + } + + @Operation(summary = "deleteResource", description = "DELETE_RESOURCE_BY_ID_NOTES") + @Parameters({ + @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class, example = "file:////tmp/dolphinscheduler/storage/default/resources/demo.sql")) + }) + @DeleteMapping() + @ResponseStatus(HttpStatus.OK) + @ApiException(DELETE_RESOURCE_ERROR) + @OperatorLog(auditType = AuditType.FILE_DELETE) + public Result deleteResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "fullName") String resourceAbsolutePath) { + DeleteResourceRequest deleteResourceRequest = DeleteResourceRequest.builder() + .loginUser(loginUser) + .resourceAbsolutePath(resourceAbsolutePath) + .build(); + resourceService.delete(deleteResourceRequest); + return Result.success(); + } + + @Operation(summary = "queryResourceFileList", description = "QUERY_RESOURCE_FILE_LIST_NOTES") + @Parameters({ + @Parameter(name = "type", description = "RESOURCE_TYPE", required = true, schema = @Schema(implementation = ResourceType.class))}) + @GetMapping(value = "/query-by-type") + @ResponseStatus(HttpStatus.OK) + @ApiException(QUERY_RESOURCES_LIST_ERROR) + public Result> queryResourceFileList(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "type") ResourceType type) { + return Result.success(resourceService.queryResourceFiles(loginUser, type)); + } + + @Operation(summary = "viewResource", description = "VIEW_RESOURCE_BY_ID_NOTES") + @Parameters({ + @Parameter(name = "fullName", description = "RESOURCE_FULL_NAME", required = true, schema = @Schema(implementation = String.class, example = "tenant/1.png")), + @Parameter(name = "tenantCode", description = "TENANT_CODE", required = true, schema = @Schema(implementation = String.class)), + @Parameter(name = "skipLineNum", description = "SKIP_LINE_NUM", required = true, schema = @Schema(implementation = int.class, example = "100")), + @Parameter(name = "limit", description = "LIMIT", required = true, schema = @Schema(implementation = int.class, example = "100"))}) + @GetMapping(value = "/view") + @ApiException(VIEW_RESOURCE_FILE_ON_LINE_ERROR) + public Result viewResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + @RequestParam(value = "fullName") String resourceAbsoluteFilePath, + @RequestParam(value = "skipLineNum") int skipLineNum, + @RequestParam(value = "limit") int limit) { + FetchFileContentRequest fetchFileContentRequest = FetchFileContentRequest.builder() + .loginUser(loginUser) + .resourceFileAbsolutePath(resourceAbsoluteFilePath) + .limit(limit == -1 ? Integer.MAX_VALUE : skipLineNum) + .skipLineNum(skipLineNum) + .build(); + return Result.success(resourceService.fetchResourceFileContent(fetchFileContentRequest)); } - /** - * download resource file - * - * @param loginUser login user - * @return resource content - */ @Operation(summary = "downloadResource", description = "DOWNLOAD_RESOURCE_NOTES") @Parameters({ @Parameter(name = "fullName", description = "RESOURCE_FULLNAME", required = true, schema = @Schema(implementation = String.class, example = "test/"))}) @GetMapping(value = "/download") @ResponseBody @ApiException(DOWNLOAD_RESOURCE_FILE_ERROR) - public ResponseEntity downloadResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, - @RequestParam(value = "fullName") String fullName) throws Exception { - Resource file = resourceService.downloadResource(loginUser, fullName); - if (file == null) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(RESOURCE_NOT_EXIST.getMsg()); - } - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"") - .body(file); + public void downloadResource(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + HttpServletResponse response, + @RequestParam(value = "fullName") String fileAbsolutePath) { + + DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder() + .loginUser(loginUser) + .fileAbsolutePath(fileAbsolutePath) + .build(); + + resourceService.downloadResource(response, downloadFileRequest); } - /** - * create udf function - * - * @param loginUser login user - * @param type udf type - * @param funcName function name - * @param argTypes argument types - * @param database database - * @param description description - * @param className class name - * @return create result code - */ @Operation(summary = "createUdfFunc", description = "CREATE_UDF_FUNCTION_NOTES") @Parameters({ @Parameter(name = "type", description = "UDF_TYPE", required = true, schema = @Schema(implementation = UdfType.class)), @@ -612,8 +527,8 @@ public class ResourcesController extends BaseController { @GetMapping(value = "/base-dir") @ResponseStatus(HttpStatus.OK) @ApiException(RESOURCE_NOT_EXIST) - public Result queryResourceBaseDir(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, + public Result queryResourceBaseDir(@Parameter(hidden = true) @RequestAttribute(value = Constants.SESSION_USER) User loginUser, @RequestParam(value = "type") ResourceType type) { - return resourceService.queryResourceBaseDir(loginUser, type); + return Result.success(resourceService.queryResourceBaseDir(loginUser, type)); } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/AbstractResourceCreateRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/AbstractResourceCreateRequest.java new file mode 100644 index 0000000000..e4e0f2fa95 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/AbstractResourceCreateRequest.java @@ -0,0 +1,34 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import lombok.Data; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +public abstract class AbstractResourceCreateRequest { + + private User loginUser; + private String parentAbsoluteDirectory; + private ResourceType type; + +} diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/BlockingOpportunity.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/AbstractResourceDto.java similarity index 70% rename from dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/BlockingOpportunity.java rename to dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/AbstractResourceDto.java index 5fc7c5073f..6314c8542c 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/BlockingOpportunity.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/AbstractResourceDto.java @@ -15,21 +15,15 @@ * limitations under the License. */ -package org.apache.dolphinscheduler.common.enums; +package org.apache.dolphinscheduler.api.dto.resources; -public enum BlockingOpportunity { +import lombok.Data; +import lombok.experimental.SuperBuilder; - BLOCKING_ON_SUCCESS("BlockingOnSuccess"), - BLOCKING_ON_FAILED("BlockingOnFailed"); +@Data +@SuperBuilder +public abstract class AbstractResourceDto { - private final String desc; - - BlockingOpportunity(String desc) { - this.desc = desc; - } - - public String getDesc() { - return desc; - } + private String resourceAbsolutePath; } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateDirectoryDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateDirectoryDto.java new file mode 100644 index 0000000000..14b8eaeac2 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateDirectoryDto.java @@ -0,0 +1,37 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CreateDirectoryDto { + + private User loginUser; + + private String directoryAbsolutePath; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateDirectoryRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateDirectoryRequest.java new file mode 100644 index 0000000000..7719fffb66 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateDirectoryRequest.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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateDirectoryRequest { + + private User loginUser; + + private ResourceType type; + + private String parentAbsoluteDirectory; + + private String directoryName; +} diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkerServer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileDto.java similarity index 61% rename from dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkerServer.java rename to dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileDto.java index 1504fdd367..da8a37dc45 100644 --- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkerServer.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileDto.java @@ -15,47 +15,27 @@ * limitations under the License. */ -package org.apache.dolphinscheduler.dao.entity; +package org.apache.dolphinscheduler.api.dto.resources; -import java.util.Date; +import org.apache.dolphinscheduler.dao.entity.User; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; + +import org.springframework.web.multipart.MultipartFile; @Data -public class WorkerServer { +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateFileDto { - /** - * id - */ - private int id; + private User loginUser; - /** - * host - */ - private String host; + private String fileAbsolutePath; - /** - * port - */ - private int port; + private MultipartFile file; - /** - * zookeeper directory - */ - private String zkDirectory; - - /** - * resource info - */ - private String resInfo; - - /** - * create time - */ - private Date createTime; - - /** - * last heart beat time - */ - private Date lastHeartbeatTime; } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileFromContentDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileFromContentDto.java new file mode 100644 index 0000000000..a8faf8b0aa --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileFromContentDto.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateFileFromContentDto { + + private User loginUser; + + private String fileAbsolutePath; + + private String fileContent; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileFromContentRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileFromContentRequest.java new file mode 100644 index 0000000000..4b424dbd89 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileFromContentRequest.java @@ -0,0 +1,40 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateFileFromContentRequest { + + private User loginUser; + private ResourceType type; + private String parentAbsoluteDirectory; + private String fileName; + private String fileContent; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileRequest.java new file mode 100644 index 0000000000..c72ffa97c4 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/CreateFileRequest.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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import org.springframework.web.multipart.MultipartFile; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateFileRequest { + + private User loginUser; + private ResourceType type; + private String parentAbsoluteDirectory; + private String fileName; + private MultipartFile file; +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DeleteResourceDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DeleteResourceDto.java new file mode 100644 index 0000000000..c79cd0d59a --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DeleteResourceDto.java @@ -0,0 +1,36 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeleteResourceDto { + + private User loginUser; + private String resourceAbsolutePath; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DeleteResourceRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DeleteResourceRequest.java new file mode 100644 index 0000000000..3af948d512 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DeleteResourceRequest.java @@ -0,0 +1,35 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class DeleteResourceRequest { + + private User loginUser; + private String resourceAbsolutePath; +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DownloadFileDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DownloadFileDto.java new file mode 100644 index 0000000000..fe5f0721cb --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DownloadFileDto.java @@ -0,0 +1,36 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DownloadFileDto { + + private User loginUser; + + private String fileAbsolutePath; +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DownloadFileRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DownloadFileRequest.java new file mode 100644 index 0000000000..1f28a162be --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/DownloadFileRequest.java @@ -0,0 +1,37 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class DownloadFileRequest { + + private User loginUser; + + private String fileAbsolutePath; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/FetchFileContentDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/FetchFileContentDto.java new file mode 100644 index 0000000000..106cbe142f --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/FetchFileContentDto.java @@ -0,0 +1,38 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FetchFileContentDto { + + private User loginUser; + private String resourceFileAbsolutePath; + private int skipLineNum; + private int limit; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/FetchFileContentRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/FetchFileContentRequest.java new file mode 100644 index 0000000000..8357317e85 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/FetchFileContentRequest.java @@ -0,0 +1,37 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FetchFileContentRequest { + + private User loginUser; + private String resourceFileAbsolutePath; + private int skipLineNum; + private int limit; +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/PagingResourceItemRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/PagingResourceItemRequest.java new file mode 100644 index 0000000000..49d720bca3 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/PagingResourceItemRequest.java @@ -0,0 +1,58 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.api.enums.Status; +import org.apache.dolphinscheduler.api.exceptions.ServiceException; +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PagingResourceItemRequest { + + private User loginUser; + + private String resourceAbsolutePath; + + private ResourceType resourceType; + + private String resourceNameKeyWord; + + Integer pageNo; + + Integer pageSize; + + public void checkPageNoAndPageSize() { + if (pageNo <= 0) { + throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.PAGE_NUMBER); + } + if (pageSize <= 0) { + throw new ServiceException(Status.REQUEST_PARAMS_NOT_VALID_ERROR, Constants.PAGE_SIZE); + } + } + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/QueryResourceDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/QueryResourceDto.java new file mode 100644 index 0000000000..7778ca50f8 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/QueryResourceDto.java @@ -0,0 +1,34 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QueryResourceDto { + + private List resourceAbsolutePaths; +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameDirectoryDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameDirectoryDto.java new file mode 100644 index 0000000000..2989947922 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameDirectoryDto.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RenameDirectoryDto { + + private User loginUser; + + private String originDirectoryAbsolutePath; + + private String targetDirectoryAbsolutePath; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameDirectoryRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameDirectoryRequest.java new file mode 100644 index 0000000000..640acb677d --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameDirectoryRequest.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RenameDirectoryRequest { + + private User loginUser; + + private String directoryAbsolutePath; + + private String newDirectoryName; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameFileDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameFileDto.java new file mode 100644 index 0000000000..091febad0e --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameFileDto.java @@ -0,0 +1,38 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RenameFileDto { + + private User loginUser; + + private String originFileAbsolutePath; + + private String targetFileAbsolutePath; +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameFileRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameFileRequest.java new file mode 100644 index 0000000000..1b0c56be3b --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/RenameFileRequest.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RenameFileRequest { + + private User loginUser; + + private String fileAbsolutePath; + + private String newFileName; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/ResourceComponent.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/ResourceComponent.java index 4487c5a080..2aa11a6ce9 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/ResourceComponent.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/ResourceComponent.java @@ -25,34 +25,13 @@ import java.util.List; import lombok.Data; import lombok.NoArgsConstructor; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - /** * resource component */ @Data @NoArgsConstructor -@JsonPropertyOrder({"id", "pid", "name", "fullName", "description", "isDirctory", "children", "type"}) public abstract class ResourceComponent { - public ResourceComponent(int id, String pid, String name, String fullName, String description, boolean isDirctory) { - this.id = id; - this.pid = pid; - this.name = name; - this.fullName = fullName; - this.isDirctory = isDirctory; - int directoryFlag = isDirctory ? 1 : 0; - this.idValue = String.format("%s_%s", id, directoryFlag); - } - - /** - * id - */ - protected int id; - /** - * parent id - */ - protected String pid; /** * name */ @@ -73,10 +52,7 @@ public abstract class ResourceComponent { * is directory */ protected boolean isDirctory; - /** - * id value - */ - protected String idValue; + /** * resoruce type */ @@ -88,14 +64,11 @@ public abstract class ResourceComponent { /** * add resource component + * * @param resourceComponent resource component */ public void add(ResourceComponent resourceComponent) { children.add(resourceComponent); } - public void setIdValue(int id, boolean isDirctory) { - int directoryFlag = isDirctory ? 1 : 0; - this.idValue = String.format("%s_%s", id, directoryFlag); - } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileDto.java new file mode 100644 index 0000000000..9213ac04d1 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileDto.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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import org.springframework.web.multipart.MultipartFile; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateFileDto { + + private User loginUser; + + private String fileAbsolutePath; + + private MultipartFile file; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileFromContentDto.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileFromContentDto.java new file mode 100644 index 0000000000..0848cd8f2b --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileFromContentDto.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateFileFromContentDto { + + private User loginUser; + + private String fileAbsolutePath; + + private String fileContent; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileFromContentRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileFromContentRequest.java new file mode 100644 index 0000000000..a9564a4b25 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileFromContentRequest.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateFileFromContentRequest { + + private User loginUser; + + private String fileAbsolutePath; + + private String fileContent; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileRequest.java new file mode 100644 index 0000000000..10fbc65700 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/UpdateFileRequest.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.dolphinscheduler.api.dto.resources; + +import org.apache.dolphinscheduler.dao.entity.User; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import org.springframework.web.multipart.MultipartFile; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateFileRequest { + + private User loginUser; + + private String fileAbsolutePath; + + private MultipartFile file; + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/visitor/ResourceTreeVisitor.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/visitor/ResourceTreeVisitor.java index 6c88cd84a8..1d24610af0 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/visitor/ResourceTreeVisitor.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/resources/visitor/ResourceTreeVisitor.java @@ -122,12 +122,8 @@ public class ResourceTreeVisitor implements Visitor { tempResourceComponent = new FileLeaf(); } - tempResourceComponent.setName(resource.getAlias()); - // tempResourceComponent.setFullName(resource.getFullName().replaceFirst("/","")); + tempResourceComponent.setName(resource.getFileName()); tempResourceComponent.setFullName(resource.getFullName()); - tempResourceComponent.setId(resource.getId()); - tempResourceComponent.setPid(resource.getPfullName()); - tempResourceComponent.setIdValue(resource.getId(), resource.isDirectory()); tempResourceComponent.setType(resource.getType()); return tempResourceComponent; } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/task/TaskCreateRequest.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/task/TaskCreateRequest.java index 1e651ee6e3..bb362962fa 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/task/TaskCreateRequest.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/task/TaskCreateRequest.java @@ -19,11 +19,11 @@ package org.apache.dolphinscheduler.api.dto.task; import static org.apache.dolphinscheduler.common.constants.Constants.VERSION_FIRST; -import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.Flag; import org.apache.dolphinscheduler.common.enums.Priority; import org.apache.dolphinscheduler.common.enums.TimeoutFlag; import org.apache.dolphinscheduler.dao.entity.TaskDefinition; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskTimeoutStrategy; import java.util.Date; @@ -107,7 +107,7 @@ public class TaskCreateRequest { taskDefinition.setProjectCode(this.projectCode); taskDefinition.setTaskType(this.taskType); taskDefinition.setTaskParams(this.taskParams); - taskDefinition.setWorkerGroup(this.workerGroup == null ? Constants.DEFAULT_WORKER_GROUP : this.workerGroup); + taskDefinition.setWorkerGroup(WorkerGroupUtils.getWorkerGroupOrDefault(workerGroup)); taskDefinition.setEnvironmentCode(this.environmentCode); taskDefinition.setFailRetryTimes(this.failRetryTimes); taskDefinition.setFailRetryInterval(this.failRetryInterval); diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/exceptions/ServiceException.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/exceptions/ServiceException.java index 2fa3e01a1c..a0f8162fcf 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/exceptions/ServiceException.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/exceptions/ServiceException.java @@ -52,4 +52,8 @@ public class ServiceException extends RuntimeException { this.code = code; } + public ServiceException(String message, Exception exception) { + this(Status.INTERNAL_SERVER_ERROR_ARGS, message, exception); + } + } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/python/PythonGateway.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/python/PythonGateway.java index 762ba576b0..a2ad908e47 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/python/PythonGateway.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/python/PythonGateway.java @@ -31,14 +31,12 @@ import org.apache.dolphinscheduler.api.service.SchedulerService; import org.apache.dolphinscheduler.api.service.TaskDefinitionService; import org.apache.dolphinscheduler.api.service.TenantService; import org.apache.dolphinscheduler.api.service.UsersService; -import org.apache.dolphinscheduler.api.utils.Result; import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.ComplementDependentMode; import org.apache.dolphinscheduler.common.enums.ExecutionOrder; import org.apache.dolphinscheduler.common.enums.FailureStrategy; import org.apache.dolphinscheduler.common.enums.Priority; import org.apache.dolphinscheduler.common.enums.ProcessExecutionTypeEnum; -import org.apache.dolphinscheduler.common.enums.ProgramType; import org.apache.dolphinscheduler.common.enums.ReleaseState; import org.apache.dolphinscheduler.common.enums.RunMode; import org.apache.dolphinscheduler.common.enums.TaskDependType; @@ -216,22 +214,22 @@ public class PythonGateway { * If workflow do not exists in Project=`projectCode` would create a new one * If workflow already exists in Project=`projectCode` would update it * - * @param userName user name who create or update workflow - * @param projectName project name which workflow belongs to - * @param name workflow name - * @param description description - * @param globalParams global params - * @param schedule schedule for workflow, will not set schedule if null, - * and if would always fresh exists schedule if not null - * @param onlineSchedule Whether set the workflow's schedule to online state - * @param warningType warning type - * @param warningGroupId warning group id - * @param timeout timeout for workflow working, if running time longer than timeout, - * task will mark as fail - * @param workerGroup run task in which worker group - * @param taskRelationJson relation json for nodes + * @param userName user name who create or update workflow + * @param projectName project name which workflow belongs to + * @param name workflow name + * @param description description + * @param globalParams global params + * @param schedule schedule for workflow, will not set schedule if null, + * and if would always fresh exists schedule if not null + * @param onlineSchedule Whether set the workflow's schedule to online state + * @param warningType warning type + * @param warningGroupId warning group id + * @param timeout timeout for workflow working, if running time longer than timeout, + * task will mark as fail + * @param workerGroup run task in which worker group + * @param taskRelationJson relation json for nodes * @param taskDefinitionJson taskDefinitionJson - * @param otherParamsJson otherParamsJson handle other params + * @param otherParamsJson otherParamsJson handle other params * @return create result code */ public Long createOrUpdateWorkflow(String userName, @@ -300,8 +298,8 @@ public class PythonGateway { /** * get workflow * - * @param user user who create or update schedule - * @param projectCode project which workflow belongs to + * @param user user who create or update schedule + * @param projectCode project which workflow belongs to * @param workflowName workflow name */ private ProcessDefinition getWorkflow(User user, long projectCode, String workflowName) { @@ -327,13 +325,13 @@ public class PythonGateway { * It would always use latest schedule define in workflow-as-code, and set schedule online when * it's not null * - * @param user user who create or update schedule - * @param projectCode project which workflow belongs to - * @param workflowCode workflow code - * @param schedule schedule expression + * @param user user who create or update schedule + * @param projectCode project which workflow belongs to + * @param workflowCode workflow code + * @param schedule schedule expression * @param onlineSchedule Whether set the workflow's schedule to online state - * @param workerGroup work group - * @param warningType warning type + * @param workerGroup work group + * @param warningType warning type * @param warningGroupId warning group id */ private void createOrUpdateSchedule(User user, @@ -512,7 +510,7 @@ public class PythonGateway { * it will return the datasource match the type. * * @param datasourceName datasource name of datasource - * @param type datasource type + * @param type datasource type */ public DataSource getDatasource(String datasourceName, String type) { @@ -545,8 +543,8 @@ public class PythonGateway { * Get workflow object by given workflow name. It returns map contain workflow id, name, code. * Useful in Python API create subProcess task which need workflow information. * - * @param userName user who create or update schedule - * @param projectName project name which workflow belongs to + * @param userName user who create or update schedule + * @param projectName project name which workflow belongs to * @param workflowName workflow name */ public Map getWorkflowInfo(String userName, String projectName, @@ -577,9 +575,9 @@ public class PythonGateway { * Get project, workflow, task code. * Useful in Python API create dependent task which need workflow information. * - * @param projectName project name which workflow belongs to + * @param projectName project name which workflow belongs to * @param workflowName workflow name - * @param taskName task name + * @param taskName task name */ public Map getDependentInfo(String projectName, String workflowName, String taskName) { Map result = new HashMap<>(); @@ -614,25 +612,22 @@ public class PythonGateway { * Get resource by given program type and full name. It returns map contain resource id, name. * Useful in Python API create flink or spark task which need workflow information. * - * @param programType program type one of SCALA, JAVA and PYTHON - * @param fullName full name of the resource + * @param fullName full name of the resource */ - public Map getResourcesFileInfo(String programType, String fullName) { + public Map getResourcesFileInfo(String fullName) { Map result = new HashMap<>(); - Result resources = resourceService.queryResourceByProgramType(dummyAdminUser, ResourceType.FILE, - ProgramType.valueOf(programType)); - List resourcesComponent = (List) resources.getData(); - List namedResources = - resourcesComponent.stream().filter(s -> fullName.equals(s.getFullName())).collect(Collectors.toList()); + List resourceComponents = + resourceService.queryResourceFiles(dummyAdminUser, ResourceType.FILE); + List namedResources = resourceComponents.stream() + .filter(s -> fullName.equals(s.getFullName())) + .collect(Collectors.toList()); if (CollectionUtils.isEmpty(namedResources)) { - String msg = - String.format("Can not find valid resource by program type %s and name %s", programType, fullName); + String msg = String.format("Can not find valid resource by name %s", fullName); log.error(msg); throw new IllegalArgumentException(msg); } - result.put("id", namedResources.get(0).getId()); result.put("name", namedResources.get(0).getName()); return result; } @@ -671,20 +666,6 @@ public class PythonGateway { return PythonGateway.class.getPackage().getImplementationVersion(); } - /** - * create or update resource. - * If the folder is not already created, it will be - * - * @param userName user who create or update resource - * @param fullName The fullname of resource.Includes path and suffix. - * @param resourceContent content of resource - * @return StorageEntity object which contains necessary information about resource - */ - public StorageEntity createOrUpdateResource(String userName, String fullName, - String resourceContent) throws Exception { - return resourceService.createOrUpdateResource(userName, fullName, resourceContent); - } - @PostConstruct public void init() { if (apiConfig.getPythonGateway().isEnabled()) { diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ResourcesService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ResourcesService.java index 54023baad9..0b501dbbc8 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ResourcesService.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/ResourcesService.java @@ -17,181 +17,99 @@ package org.apache.dolphinscheduler.api.service; -import org.apache.dolphinscheduler.api.dto.resources.DeleteDataTransferResponse; +import org.apache.dolphinscheduler.api.dto.resources.CreateDirectoryRequest; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileFromContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.DeleteResourceRequest; +import org.apache.dolphinscheduler.api.dto.resources.DownloadFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.FetchFileContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.PagingResourceItemRequest; +import org.apache.dolphinscheduler.api.dto.resources.RenameDirectoryRequest; +import org.apache.dolphinscheduler.api.dto.resources.RenameFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.ResourceComponent; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileFromContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileRequest; import org.apache.dolphinscheduler.api.utils.PageInfo; -import org.apache.dolphinscheduler.api.utils.Result; -import org.apache.dolphinscheduler.common.enums.ProgramType; +import org.apache.dolphinscheduler.api.vo.ResourceItemVO; +import org.apache.dolphinscheduler.api.vo.resources.FetchFileContentResponse; import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; import org.apache.dolphinscheduler.spi.enums.ResourceType; -import java.io.IOException; -import java.util.Map; +import java.util.List; -import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; -/** - * resources service - */ public interface ResourcesService { /** - * create directory - * - * @param loginUser login user - * @param name alias - * @param type type - * @param pid parent id - * @param currentDir current directory - * @return create directory result + * Create a new directory in the resource storage, if the directory already exists will throw exception */ - Result createDirectory(User loginUser, - String name, - ResourceType type, - int pid, - String currentDir); + void createDirectory(CreateDirectoryRequest createDirectoryRequest); /** - * create resource - * - * @param loginUser login user - * @param name alias - * @param type type - * @param file file - * @param currentDir current directory - * @return create result code + * Rename the directory in the resource storage, if the origin directory not exists or the new directory already exists will throw exception. + *

If the origin directory is empty will only update the directory name. + *

If the origin directory is not empty will move all the files and directories to the new directory. + *

After update the origin directory will be deleted. */ - Result uploadResource(User loginUser, - String name, - ResourceType type, - MultipartFile file, - String currentDir); + void renameDirectory(RenameDirectoryRequest renameDirectoryRequest); /** - * update resource - * @param loginUser login user - * @param name name - * @param type resource type - * @param file resource file - * @return update result code + * Upload a new file to the resource storage, if the file already exists will throw exception */ - Result updateResource(User loginUser, - String fullName, - String tenantCode, - String name, - ResourceType type, - MultipartFile file); + void createFile(CreateFileRequest createFileRequest); /** - * query resources list paging - * - * @param loginUser login user - * @param type resource type - * @param searchVal search value - * @param pageNo page number - * @param pageSize page size - * @return resource list page + * Update the file in the resource storage, if the origin file not exists or the new file already exists will throw exception. + *

If the new file is empty will only update the file name. + *

If the new file is not empty will update the file content and name. + *

After update the origin file will be deleted. */ - Result> queryResourceListPaging(User loginUser, String fullName, String resTenantCode, - ResourceType type, String searchVal, Integer pageNo, - Integer pageSize); + void updateFile(UpdateFileRequest updateFileRequest); /** - * query resource list - * - * @param loginUser login user - * @param type resource type - * @return resource list + * Rename the file in the resource storage, if the origin file not exists or the new file already exists will throw exception. */ - Map queryResourceList(User loginUser, ResourceType type, String fullName); + void renameFile(RenameFileRequest renameFileRequest); /** - * query resource list by program type - * - * @param loginUser login user - * @param type resource type - * @return resource list + * Create a new file in the resource storage, if the file already exists will throw exception. + * Different with {@link ResourcesService#createFile(CreateFileRequest)} this method will create a new file with the given content. */ - Result queryResourceByProgramType(User loginUser, ResourceType type, ProgramType programType); + void createFileFromContent(CreateFileFromContentRequest createFileFromContentRequest); /** - * delete resource - * - * @param loginUser login user - * @return delete result code - * @throws IOException exception + * Update the file content. */ - Result delete(User loginUser, String fullName, String tenantCode) throws IOException; + void updateFileFromContent(UpdateFileFromContentRequest updateFileContentRequest); /** - * verify resource by name and type - * @param loginUser login user - * @param fullName resource full name - * @param type resource type - * @return true if the resource name not exists, otherwise return false + * Paging query resource items. + *

If the login user is not admin will only query the resource items that under the user's tenant. + *

If the login user is admin and {@link PagingResourceItemRequest##resourceAbsolutePath} is null will return all the resource items. */ - Result verifyResourceName(String fullName, ResourceType type, User loginUser); + PageInfo pagingResourceItem(PagingResourceItemRequest pagingResourceItemRequest); /** - * verify resource by file name - * @param fileName resource file name - * @param type resource type - * @return true if the resource file name, otherwise return false + * Query the resource file items by the given resource type and program type. */ - Result queryResourceByFileName(User loginUser, String fileName, ResourceType type, String resTenantCode); + List queryResourceFiles(User loginUser, ResourceType type); /** - * view resource file online - * - * @param skipLineNum skip line number - * @param limit limit - * @param fullName fullName - * @return resource content + * Delete the resource item. + *

If the resource item is a directory will delete all the files and directories under the directory. + *

If the resource item is a file will delete the file. + *

If the resource item not exists will throw exception. */ - Result readResource(User loginUser, String fullName, String tenantCode, int skipLineNum, int limit); + void delete(DeleteResourceRequest deleteResourceRequest); /** - * create resource file online - * - * @param loginUser login user - * @param type resource type - * @param fileName file name - * @param fileSuffix file suffix - * @param content content - * @return create result code + * Fetch the file content. */ - Result createResourceFile(User loginUser, ResourceType type, String fileName, String fileSuffix, - String content, String currentDirectory); + FetchFileContentResponse fetchResourceFileContent(FetchFileContentRequest fetchFileContentRequest); - /** - * create or update resource. - * If the folder is not already created, it will be ignored and directly create the new file - * - * @param userName user who create or update resource - * @param fullName The fullname of resource.Includes path and suffix. - * @param resourceContent content of resource - */ - StorageEntity createOrUpdateResource(String userName, String fullName, String resourceContent) throws Exception; - - /** - * updateProcessInstance resource - * - * @param loginUser login user - * @param fullName full name - * @param tenantCode tenantCode - * @param content content - * @return update result cod - */ - Result updateResourceContent(User loginUser, String fullName, String tenantCode, - String content); - - /** - * download file - * - * @return resource content - * @throws IOException exception - */ - org.springframework.core.io.Resource downloadResource(User loginUser, String fullName) throws IOException; + void downloadResource(HttpServletResponse response, DownloadFileRequest downloadFileRequest); /** * Get resource by given resource type and file name. @@ -202,21 +120,6 @@ public interface ResourcesService { */ StorageEntity queryFileStatus(String userName, String fileName) throws Exception; - /** - * delete DATA_TRANSFER data in resource center - * - * @param loginUser user who query resource - * @param days number of days - */ - DeleteDataTransferResponse deleteDataTransferData(User loginUser, Integer days); - - /** - * get resource base dir - * - * @param loginUser login user - * @param type resource type - * @return - */ - Result queryResourceBaseDir(User loginUser, ResourceType type); + String queryResourceBaseDir(User loginUser, ResourceType type); } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/WorkerGroupService.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/WorkerGroupService.java index 2c87e4be2e..b85d3912cb 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/WorkerGroupService.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/WorkerGroupService.java @@ -18,7 +18,6 @@ package org.apache.dolphinscheduler.api.service; import org.apache.dolphinscheduler.api.utils.Result; -import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.dao.entity.User; import java.util.List; @@ -77,13 +76,6 @@ public interface WorkerGroupService { */ Map getWorkerAddressList(); - /** - * Get task instance's worker group - * @param taskInstance task instance - * @return worker group - */ - String getTaskWorkerGroup(TaskInstance taskInstance); - /** * Query worker group by process definition codes * @param processDefinitionCodeList processDefinitionCodeList diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ExecutorServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ExecutorServiceImpl.java index 3e613b11fa..6bb7d228ff 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ExecutorServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ExecutorServiceImpl.java @@ -89,8 +89,8 @@ import org.apache.dolphinscheduler.extract.master.dto.WorkflowExecuteDto; import org.apache.dolphinscheduler.extract.master.transportor.StreamingTaskTriggerRequest; import org.apache.dolphinscheduler.extract.master.transportor.StreamingTaskTriggerResponse; import org.apache.dolphinscheduler.extract.master.transportor.WorkflowInstanceStateChangeEvent; -import org.apache.dolphinscheduler.plugin.task.api.TaskConstants; import org.apache.dolphinscheduler.plugin.task.api.model.Property; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType; import org.apache.dolphinscheduler.service.command.CommandService; import org.apache.dolphinscheduler.service.cron.CronUtils; @@ -361,7 +361,7 @@ public class ExecutorServiceImpl extends BaseServiceImpl implements ExecutorServ // find out the process definition code Set processDefinitionCodeSet = new HashSet<>(); taskDefinitions.stream() - .filter(task -> TaskConstants.TASK_TYPE_SUB_PROCESS.equalsIgnoreCase(task.getTaskType())).forEach( + .filter(task -> TaskTypeUtils.isSubWorkflowTask(task.getTaskType())).forEach( taskDefinition -> processDefinitionCodeSet.add(Long.valueOf( JSONUtils.getNodeString(taskDefinition.getTaskParams(), CMD_PARAM_SUB_PROCESS_DEFINE_CODE)))); diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessDefinitionServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessDefinitionServiceImpl.java index e6893b04fb..820d35b3b1 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessDefinitionServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessDefinitionServiceImpl.java @@ -34,13 +34,12 @@ import static org.apache.dolphinscheduler.api.enums.Status.PROCESS_DEFINE_NOT_EX import static org.apache.dolphinscheduler.common.constants.CommandKeyConstants.CMD_PARAM_SUB_PROCESS_DEFINE_CODE; import static org.apache.dolphinscheduler.common.constants.Constants.COPY_SUFFIX; import static org.apache.dolphinscheduler.common.constants.Constants.DATA_LIST; -import static org.apache.dolphinscheduler.common.constants.Constants.DEFAULT_WORKER_GROUP; import static org.apache.dolphinscheduler.common.constants.Constants.GLOBAL_PARAMS; import static org.apache.dolphinscheduler.common.constants.Constants.IMPORT_SUFFIX; import static org.apache.dolphinscheduler.common.constants.Constants.LOCAL_PARAMS; import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.LOCAL_PARAMS_LIST; import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SQL; +import static org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager.checkTaskParameters; import org.apache.dolphinscheduler.api.dto.DagDataSchedule; import org.apache.dolphinscheduler.api.dto.treeview.Instance; @@ -109,12 +108,13 @@ import org.apache.dolphinscheduler.dao.model.PageListingResult; import org.apache.dolphinscheduler.dao.repository.ProcessDefinitionDao; import org.apache.dolphinscheduler.dao.repository.ProcessDefinitionLogDao; import org.apache.dolphinscheduler.dao.repository.TaskDefinitionLogDao; -import org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.plugin.task.api.enums.SqlType; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskTimeoutStrategy; import org.apache.dolphinscheduler.plugin.task.api.model.Property; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.plugin.task.api.parameters.SqlParameters; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; +import org.apache.dolphinscheduler.plugin.task.sql.SqlTaskChannelFactory; import org.apache.dolphinscheduler.service.alert.ListenerEventAlertManager; import org.apache.dolphinscheduler.service.model.TaskNode; import org.apache.dolphinscheduler.service.process.ProcessService; @@ -421,11 +421,7 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro throw new ServiceException(Status.DATA_IS_NOT_VALID, taskDefinitionJson); } for (TaskDefinitionLog taskDefinitionLog : taskDefinitionLogs) { - if (!TaskPluginManager.checkTaskParameters(ParametersNode.builder() - .taskType(taskDefinitionLog.getTaskType()) - .taskParams(taskDefinitionLog.getTaskParams()) - .dependence(taskDefinitionLog.getDependence()) - .build())) { + if (!checkTaskParameters(taskDefinitionLog.getTaskType(), taskDefinitionLog.getTaskParams())) { log.error( "Generate task definition list failed, the given task definition parameter is invalided, taskName: {}, taskDefinition: {}", taskDefinitionLog.getName(), taskDefinitionLog); @@ -1386,11 +1382,11 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro sqlParameters.setLocalParams(Collections.emptyList()); taskDefinition.setTaskParams(JSONUtils.toJsonString(sqlParameters)); taskDefinition.setCode(CodeGenerateUtils.genCode()); - taskDefinition.setTaskType(TASK_TYPE_SQL); + taskDefinition.setTaskType(SqlTaskChannelFactory.NAME); taskDefinition.setFailRetryTimes(0); taskDefinition.setFailRetryInterval(0); taskDefinition.setTimeoutFlag(TimeoutFlag.CLOSE); - taskDefinition.setWorkerGroup(DEFAULT_WORKER_GROUP); + taskDefinition.setWorkerGroup(WorkerGroupUtils.getDefaultWorkerGroup()); taskDefinition.setTaskPriority(Priority.MEDIUM); taskDefinition.setEnvironmentCode(-1); taskDefinition.setTimeout(0); @@ -1615,13 +1611,7 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro // check whether the process definition json is normal for (TaskNode taskNode : taskNodes) { - if (!TaskPluginManager.checkTaskParameters(ParametersNode.builder() - .taskType(taskNode.getType()) - .taskParams(taskNode.getTaskParams()) - .dependence(taskNode.getDependence()) - .switchResult(taskNode.getSwitchResult()) - .build())) { - log.error("Task node {} parameter invalid.", taskNode.getName()); + if (!checkTaskParameters(taskNode.getType(), taskNode.getParams())) { putMsg(result, Status.PROCESS_NODE_S_PARAMETER_INVALID, taskNode.getName()); return result; } @@ -1891,7 +1881,7 @@ public class ProcessDefinitionServiceImpl extends BaseServiceImpl implements Pro long subProcessCode = 0L; // if process is sub process, the return sub id, or sub id=0 - if (taskInstance.isSubProcess()) { + if (TaskTypeUtils.isSubWorkflowTask(taskInstance.getTaskType())) { TaskDefinition taskDefinition = taskDefinitionMap.get(taskInstance.getTaskCode()); subProcessCode = Long.parseLong(JSONUtils.parseObject( taskDefinition.getTaskParams()).path(CMD_PARAM_SUB_PROCESS_DEFINE_CODE).asText()); diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessInstanceServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessInstanceServiceImpl.java index 06e9fd95db..5e5d703017 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessInstanceServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessInstanceServiceImpl.java @@ -26,8 +26,7 @@ import static org.apache.dolphinscheduler.common.constants.Constants.GLOBAL_PARA import static org.apache.dolphinscheduler.common.constants.Constants.LOCAL_PARAMS; import static org.apache.dolphinscheduler.common.constants.Constants.PROCESS_INSTANCE_STATE; import static org.apache.dolphinscheduler.common.constants.Constants.TASK_LIST; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_DEPENDENT; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SUB_PROCESS; +import static org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager.checkTaskParameters; import org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant; import org.apache.dolphinscheduler.api.dto.DynamicSubWorkflowDto; @@ -77,11 +76,10 @@ import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; import org.apache.dolphinscheduler.dao.repository.ProcessInstanceMapDao; import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; import org.apache.dolphinscheduler.dao.utils.WorkflowUtils; -import org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager; import org.apache.dolphinscheduler.plugin.task.api.enums.DependResult; import org.apache.dolphinscheduler.plugin.task.api.model.Property; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.plugin.task.api.utils.ParameterUtils; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import org.apache.dolphinscheduler.service.expand.CuringParamsService; import org.apache.dolphinscheduler.service.model.TaskNode; import org.apache.dolphinscheduler.service.process.ProcessService; @@ -498,7 +496,7 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl implements Proce throw new ServiceException(Status.TASK_INSTANCE_NOT_EXISTS, taskId); } - if (!taskInstance.isDynamic()) { + if (!TaskTypeUtils.isDynamicTask(taskInstance.getTaskType())) { putMsg(result, Status.TASK_INSTANCE_NOT_DYNAMIC_TASK, taskInstance.getName()); throw new ServiceException(Status.TASK_INSTANCE_NOT_EXISTS, taskId); } @@ -548,7 +546,7 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl implements Proce */ private void addDependResultForTaskList(User loginUser, List taskInstanceList) throws IOException { for (TaskInstance taskInstance : taskInstanceList) { - if (TASK_TYPE_DEPENDENT.equalsIgnoreCase(taskInstance.getTaskType())) { + if (TaskTypeUtils.isDependentTask(taskInstance.getTaskType())) { log.info("DEPENDENT type task instance need to set dependent result, taskCode:{}, taskInstanceId:{}", taskInstance.getTaskCode(), taskInstance.getId()); // TODO The result of dependent item should not be obtained from the log, waiting for optimization. @@ -628,9 +626,7 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl implements Proce return result; } - if (!taskInstance.isSubProcess()) { - log.warn("Task instance is not {} type instance, projectCode:{}, taskInstanceId:{}.", - TASK_TYPE_SUB_PROCESS, projectCode, taskId); + if (!TaskTypeUtils.isSubWorkflowTask(taskInstance.getTaskType())) { putMsg(result, Status.TASK_INSTANCE_NOT_SUB_WORKFLOW_INSTANCE, taskInstance.getName()); return result; } @@ -714,11 +710,7 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl implements Proce return result; } for (TaskDefinitionLog taskDefinitionLog : taskDefinitionLogs) { - if (!TaskPluginManager.checkTaskParameters(ParametersNode.builder() - .taskType(taskDefinitionLog.getTaskType()) - .taskParams(taskDefinitionLog.getTaskParams()) - .dependence(taskDefinitionLog.getDependence()) - .build())) { + if (!checkTaskParameters(taskDefinitionLog.getTaskType(), taskDefinitionLog.getTaskParams())) { log.error("Task parameters are invalid, taskDefinitionName:{}.", taskDefinitionLog.getName()); putMsg(result, Status.PROCESS_NODE_S_PARAMETER_INVALID, taskDefinitionLog.getName()); return result; diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessTaskRelationServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessTaskRelationServiceImpl.java index d8c568e50a..1f9fe6ff53 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessTaskRelationServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProcessTaskRelationServiceImpl.java @@ -18,9 +18,6 @@ package org.apache.dolphinscheduler.api.service.impl; import static java.util.stream.Collectors.toSet; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_CONDITIONS; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_DEPENDENT; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SUB_PROCESS; import org.apache.dolphinscheduler.api.dto.taskRelation.TaskRelationCreateRequest; import org.apache.dolphinscheduler.api.dto.taskRelation.TaskRelationFilterRequest; @@ -46,6 +43,7 @@ import org.apache.dolphinscheduler.dao.mapper.ProcessTaskRelationMapper; import org.apache.dolphinscheduler.dao.mapper.ProjectMapper; import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionLogMapper; import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import org.apache.dolphinscheduler.service.process.ProcessService; import org.apache.commons.collections4.CollectionUtils; @@ -354,9 +352,9 @@ public class ProcessTaskRelationServiceImpl extends BaseServiceImpl implements P } updateProcessDefiniteVersion(loginUser, result, processDefinition); updateRelation(loginUser, result, processDefinition, processTaskRelationList); - if (TASK_TYPE_CONDITIONS.equals(taskDefinition.getTaskType()) - || TASK_TYPE_DEPENDENT.equals(taskDefinition.getTaskType()) - || TASK_TYPE_SUB_PROCESS.equals(taskDefinition.getTaskType())) { + if (TaskTypeUtils.isConditionTask(taskDefinition.getTaskType()) + || TaskTypeUtils.isSubWorkflowTask(taskDefinition.getTaskType()) + || TaskTypeUtils.isDependentTask(taskDefinition.getTaskType())) { int deleteTaskDefinition = taskDefinitionMapper.deleteByCode(taskCode); if (0 == deleteTaskDefinition) { log.error("Delete task definition error, taskDefinitionCode:{}.", taskCode); diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProjectWorkerGroupRelationServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProjectWorkerGroupRelationServiceImpl.java index 31dc113dbb..c11915667f 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProjectWorkerGroupRelationServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ProjectWorkerGroupRelationServiceImpl.java @@ -32,6 +32,7 @@ import org.apache.dolphinscheduler.dao.mapper.ProjectWorkerGroupMapper; import org.apache.dolphinscheduler.dao.mapper.ScheduleMapper; import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper; import org.apache.dolphinscheduler.dao.mapper.WorkerGroupMapper; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections4.SetUtils; @@ -118,7 +119,7 @@ public class ProjectWorkerGroupRelationServiceImpl extends BaseServiceImpl workerGroupMapper.queryAllWorkerGroup().stream().map(WorkerGroup::getName).collect( Collectors.toSet()); - workerGroupNames.add(Constants.DEFAULT_WORKER_GROUP); + workerGroupNames.add(WorkerGroupUtils.getDefaultWorkerGroup()); Set assignedWorkerGroupNames = new HashSet<>(workerGroups); diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ResourcesServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ResourcesServiceImpl.java index 6a15da17a8..0757acd816 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ResourcesServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ResourcesServiceImpl.java @@ -17,16 +17,29 @@ package org.apache.dolphinscheduler.api.service.impl; -import static org.apache.dolphinscheduler.common.constants.Constants.ALIAS; -import static org.apache.dolphinscheduler.common.constants.Constants.CONTENT; -import static org.apache.dolphinscheduler.common.constants.Constants.EMPTY_STRING; -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_SS; -import static org.apache.dolphinscheduler.common.constants.Constants.JAR; -import static org.apache.dolphinscheduler.common.constants.Constants.PERIOD; - -import org.apache.dolphinscheduler.api.dto.resources.DeleteDataTransferResponse; -import org.apache.dolphinscheduler.api.dto.resources.filter.ResourceFilter; +import org.apache.dolphinscheduler.api.dto.resources.CreateDirectoryDto; +import org.apache.dolphinscheduler.api.dto.resources.CreateDirectoryRequest; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileDto; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileFromContentDto; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileFromContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.DeleteResourceDto; +import org.apache.dolphinscheduler.api.dto.resources.DeleteResourceRequest; +import org.apache.dolphinscheduler.api.dto.resources.DownloadFileDto; +import org.apache.dolphinscheduler.api.dto.resources.DownloadFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.FetchFileContentDto; +import org.apache.dolphinscheduler.api.dto.resources.FetchFileContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.PagingResourceItemRequest; +import org.apache.dolphinscheduler.api.dto.resources.QueryResourceDto; +import org.apache.dolphinscheduler.api.dto.resources.RenameDirectoryDto; +import org.apache.dolphinscheduler.api.dto.resources.RenameDirectoryRequest; +import org.apache.dolphinscheduler.api.dto.resources.RenameFileDto; +import org.apache.dolphinscheduler.api.dto.resources.RenameFileRequest; +import org.apache.dolphinscheduler.api.dto.resources.ResourceComponent; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileDto; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileFromContentDto; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileFromContentRequest; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileRequest; import org.apache.dolphinscheduler.api.dto.resources.visitor.ResourceTreeVisitor; import org.apache.dolphinscheduler.api.dto.resources.visitor.Visitor; import org.apache.dolphinscheduler.api.enums.Status; @@ -34,1269 +47,374 @@ import org.apache.dolphinscheduler.api.exceptions.ServiceException; import org.apache.dolphinscheduler.api.metrics.ApiServerMetrics; import org.apache.dolphinscheduler.api.service.ResourcesService; import org.apache.dolphinscheduler.api.utils.PageInfo; -import org.apache.dolphinscheduler.api.utils.RegexUtils; -import org.apache.dolphinscheduler.api.utils.Result; -import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ProgramType; -import org.apache.dolphinscheduler.common.enums.ResUploadType; +import org.apache.dolphinscheduler.api.validator.resource.CreateDirectoryDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.CreateDirectoryRequestTransformer; +import org.apache.dolphinscheduler.api.validator.resource.CreateFileDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.CreateFileFromContentDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.DeleteResourceDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.DownloadFileDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.FetchFileContentDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.FileFromContentRequestTransformer; +import org.apache.dolphinscheduler.api.validator.resource.FileRequestTransformer; +import org.apache.dolphinscheduler.api.validator.resource.PagingResourceItemRequestTransformer; +import org.apache.dolphinscheduler.api.validator.resource.RenameDirectoryDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.RenameDirectoryRequestTransformer; +import org.apache.dolphinscheduler.api.validator.resource.RenameFileDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.RenameFileRequestTransformer; +import org.apache.dolphinscheduler.api.validator.resource.UpdateFileDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.UpdateFileFromContentDtoValidator; +import org.apache.dolphinscheduler.api.validator.resource.UpdateFileFromContentRequestTransformer; +import org.apache.dolphinscheduler.api.validator.resource.UpdateFileRequestTransformer; +import org.apache.dolphinscheduler.api.vo.ResourceItemVO; +import org.apache.dolphinscheduler.api.vo.resources.FetchFileContentResponse; import org.apache.dolphinscheduler.common.utils.FileUtils; -import org.apache.dolphinscheduler.common.utils.PropertyUtils; import org.apache.dolphinscheduler.dao.entity.Tenant; -import org.apache.dolphinscheduler.dao.entity.UdfFunc; import org.apache.dolphinscheduler.dao.entity.User; -import org.apache.dolphinscheduler.dao.mapper.TenantMapper; -import org.apache.dolphinscheduler.dao.mapper.UdfFuncMapper; import org.apache.dolphinscheduler.dao.mapper.UserMapper; +import org.apache.dolphinscheduler.dao.repository.TenantDao; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.spi.enums.ResourceType; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import java.io.File; -import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Paths; -import java.text.MessageFormat; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; + +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import com.google.common.io.Files; - @Service @Slf4j public class ResourcesServiceImpl extends BaseServiceImpl implements ResourcesService { @Autowired - private UdfFuncMapper udfFunctionMapper; - - @Autowired - private TenantMapper tenantMapper; + private TenantDao tenantDao; @Autowired private UserMapper userMapper; - @Autowired(required = false) - private StorageOperate storageOperate; + @Autowired + private StorageOperator storageOperator; + + @Autowired + private CreateDirectoryRequestTransformer createDirectoryRequestTransformer; + + @Autowired + private CreateDirectoryDtoValidator createDirectoryDtoValidator; + + @Autowired + private RenameDirectoryRequestTransformer renameDirectoryRequestTransformer; + + @Autowired + private RenameDirectoryDtoValidator renameDirectoryDtoValidator; + + @Autowired + private RenameFileRequestTransformer renameFileRequestTransformer; + + @Autowired + private RenameFileDtoValidator renameFileDtoValidator; + + @Autowired + private FileFromContentRequestTransformer createFileFromContentRequestTransformer; + + @Autowired + private CreateFileFromContentDtoValidator createFileFromContentDtoValidator; + + @Autowired + private FetchFileContentDtoValidator fetchFileContentDtoValidator; + + @Autowired + private UpdateFileFromContentRequestTransformer updateFileFromContentRequestTransformer; + + @Autowired + private UpdateFileFromContentDtoValidator updateFileFromContentDtoValidator; + + @Autowired + private FileRequestTransformer createFileRequestTransformer; + + @Autowired + private CreateFileDtoValidator createFileDtoValidator; + + @Autowired + private UpdateFileRequestTransformer updateFileRequestTransformer; + + @Autowired + private UpdateFileDtoValidator updateFileDtoValidator; + + @Autowired + private DeleteResourceDtoValidator deleteResourceDtoValidator; + + @Autowired + private DownloadFileDtoValidator downloadFileDtoValidator; + + @Autowired + private PagingResourceItemRequestTransformer pagingResourceItemRequestTransformer; - /** - * create directory - * - * @param loginUser login user - * @param name alias - * @param type type - * @param pid parent id - * @param currentDir current directory - * @return create directory result - */ @Override - @Transactional - public Result createDirectory(User loginUser, String name, ResourceType type, int pid, String currentDir) { - Result result = new Result<>(); - if (FileUtils.directoryTraversal(name)) { - log.warn("Parameter name is invalid, name:{}.", RegexUtils.escapeNRT(name)); - putMsg(result, Status.VERIFY_PARAMETER_NAME_FAILED); - return result; - } + public void createDirectory(CreateDirectoryRequest createDirectoryRequest) { + CreateDirectoryDto createDirectoryDto = createDirectoryRequestTransformer.transform(createDirectoryRequest); + createDirectoryDtoValidator.validate(createDirectoryDto); - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, currentDir); - - String userResRootPath = ResourceType.UDF.equals(type) ? storageOperate.getUdfDir(tenantCode) - : storageOperate.getResDir(tenantCode); - String fullName = !currentDir.contains(userResRootPath) ? userResRootPath + name : currentDir + name; - - try { - if (checkResourceExists(fullName)) { - log.error("resource directory {} has exist, can't recreate", fullName); - putMsg(result, Status.RESOURCE_EXIST); - return result; - } - } catch (Exception e) { - log.warn("Resource exists, can not create again, fullName:{}.", fullName, e); - throw new ServiceException("resource already exists, can't recreate"); - } - - // create directory in storage - createDirectory(loginUser, fullName, type, result); - return result; + storageOperator.createStorageDir(createDirectoryDto.getDirectoryAbsolutePath()); + log.info("Success create directory: {}", createDirectoryRequest.getParentAbsoluteDirectory()); } - /** - * create resource - * - * @param loginUser login user - * @param name alias - * @param type type - * @param file file - * @param currentDir current directory - * @return create result code - */ @Override - @Transactional - public Result uploadResource(User loginUser, String name, ResourceType type, MultipartFile file, - String currentDir) { - Result result = new Result<>(); - - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, currentDir); - - result = verifyFile(name, type, file); - if (!result.getCode().equals(Status.SUCCESS.getCode())) { - return result; - } - - // check resource name exists - String userResRootPath = ResourceType.UDF.equals(type) ? storageOperate.getUdfDir(tenantCode) - : storageOperate.getResDir(tenantCode); - String currDirNFileName = !currentDir.contains(userResRootPath) ? userResRootPath + name : currentDir + name; + public void createFile(CreateFileRequest createFileRequest) { + CreateFileDto createFileDto = createFileRequestTransformer.transform(createFileRequest); + createFileDtoValidator.validate(createFileDto); + // todo: use storage proxy + MultipartFile file = createFileDto.getFile(); + String fileAbsolutePath = createFileDto.getFileAbsolutePath(); + String srcLocalTmpFileAbsolutePath = copyFileToLocal(file); try { - if (checkResourceExists(currDirNFileName)) { - log.error("resource {} has exist, can't recreate", RegexUtils.escapeNRT(name)); - putMsg(result, Status.RESOURCE_EXIST); - return result; - } - } catch (Exception e) { - throw new ServiceException("resource already exists, can't recreate"); - } - if (currDirNFileName.length() > Constants.RESOURCE_FULL_NAME_MAX_LENGTH) { - log.error( - "Resource file's name is longer than max full name length, fullName:{}, " - + "fullNameSize:{}, maxFullNameSize:{}", - RegexUtils.escapeNRT(name), currDirNFileName.length(), Constants.RESOURCE_FULL_NAME_MAX_LENGTH); - putMsg(result, Status.RESOURCE_FULL_NAME_TOO_LONG_ERROR); - return result; - } - - // fail upload - if (!upload(loginUser, currDirNFileName, file, type)) { - log.error("upload resource: {} file: {} failed.", RegexUtils.escapeNRT(name), - RegexUtils.escapeNRT(file.getOriginalFilename())); - putMsg(result, Status.STORE_OPERATE_CREATE_ERROR); - throw new ServiceException( - String.format("upload resource: %s file: %s failed.", name, file.getOriginalFilename())); - } else + storageOperator.upload(srcLocalTmpFileAbsolutePath, fileAbsolutePath, true, false); ApiServerMetrics.recordApiResourceUploadSize(file.getSize()); - log.info("Upload resource file complete, resourceName:{}, fileName:{}.", RegexUtils.escapeNRT(name), - RegexUtils.escapeNRT(file.getOriginalFilename())); - putMsg(result, Status.SUCCESS); - return result; - } - - /** - * check resource is exists - * - * @param fullName fullName - * @return true if resource exists - */ - private boolean checkResourceExists(String fullName) { - Boolean existResource = false; - try { - existResource = storageOperate.exists(fullName); - } catch (IOException e) { - log.error("error occurred when checking resource: " + fullName, e); + log.info("Success upload resource file: {} complete.", fileAbsolutePath); + } catch (Exception ex) { + // If exception, clear the tmp path + FileUtils.deleteFile(srcLocalTmpFileAbsolutePath); + throw ex; } - return Boolean.TRUE.equals(existResource); } - /** - * update resource - * - * @param loginUser login user - * @param resourceFullName resource full name - * @param resTenantCode tenantCode in the request field "resTenantCode" for tenant code owning the resource, - * can be different from the login user in the case of logging in as admin users. - * @param name name - * @param type resource type - * @param file resource file - * @return update result code - */ @Override - @Transactional - public Result updateResource(User loginUser, String resourceFullName, String resTenantCode, String name, - ResourceType type, MultipartFile file) { - Result result = new Result<>(); + public void createFileFromContent(CreateFileFromContentRequest createFileFromContentRequest) { + CreateFileFromContentDto createFileFromContentDto = + createFileFromContentRequestTransformer.transform(createFileFromContentRequest); + createFileFromContentDtoValidator.validate(createFileFromContentDto); - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, resourceFullName); - - if (!isUserTenantValid(isAdmin(loginUser), tenantCode, resTenantCode)) { - log.error("current user does not have permission"); - putMsg(result, Status.NO_CURRENT_OPERATING_PERMISSION); - return result; - } - - String defaultPath = storageOperate.getDir(type, tenantCode); - - StorageEntity resource; + // todo: use storage proxy + String fileContent = createFileFromContentDto.getFileContent(); + String fileAbsolutePath = createFileFromContentDto.getFileAbsolutePath(); + String srcLocalTmpFileAbsolutePath = copyFileToLocal(fileContent); try { - resource = storageOperate.getFileStatus(resourceFullName, defaultPath, resTenantCode, type); - } catch (Exception e) { - log.error("Get file status fail, resource path: {}", resourceFullName, e); - putMsg(result, Status.RESOURCE_NOT_EXIST); - throw new ServiceException((String.format("Get file status fail, resource path: %s", resourceFullName))); + storageOperator.upload(srcLocalTmpFileAbsolutePath, fileAbsolutePath, true, false); + ApiServerMetrics.recordApiResourceUploadSize(fileContent.length()); + log.info("Success upload resource file: {} complete.", fileAbsolutePath); + } catch (Exception ex) { + // If exception, clear the tmp path + FileUtils.deleteFile(srcLocalTmpFileAbsolutePath); + throw ex; } + } - // TODO: deal with OSS - if (resource.isDirectory() && storageOperate.returnStorageType().equals(ResUploadType.S3) - && !resource.getFileName().equals(name)) { - log.warn("Directory in S3 storage can not be renamed."); - putMsg(result, Status.S3_CANNOT_RENAME); - return result; - } + @Override + public void renameDirectory(RenameDirectoryRequest renameDirectoryRequest) { + RenameDirectoryDto renameDirectoryDto = renameDirectoryRequestTransformer.transform(renameDirectoryRequest); + renameDirectoryDtoValidator.validate(renameDirectoryDto); - // check if updated name of the resource already exists - String originFullName = resource.getFullName(); - String originResourceName = resource.getAlias(); + String originDirectoryAbsolutePath = renameDirectoryDto.getOriginDirectoryAbsolutePath(); + String targetDirectoryAbsolutePath = renameDirectoryDto.getTargetDirectoryAbsolutePath(); + storageOperator.copy(originDirectoryAbsolutePath, targetDirectoryAbsolutePath, true, true); + log.info("Success rename directory: {} -> {} ", originDirectoryAbsolutePath, targetDirectoryAbsolutePath); + } - // the format of hdfs folders in the implementation has a "/" at the very end, we need to remove it. - originFullName = originFullName.endsWith("/") ? StringUtils.chop(originFullName) : originFullName; - name = name.endsWith("/") ? StringUtils.chop(name) : name; - // updated fullName - String fullName = String.format(FORMAT_SS, - originFullName.substring(0, originFullName.lastIndexOf(FOLDER_SEPARATOR) + 1), name); - if (!originResourceName.equals(name)) { - try { - if (checkResourceExists(fullName)) { - log.error("resource {} already exists, can't recreate", fullName); - putMsg(result, Status.RESOURCE_EXIST); - return result; - } - } catch (Exception e) { - throw new ServiceException(String.format("error occurs while querying resource: %s", fullName)); - } + @Override + public void renameFile(RenameFileRequest renameFileRequest) { + RenameFileDto renameFileDto = renameFileRequestTransformer.transform(renameFileRequest); + renameFileDtoValidator.validate(renameFileDto); - } + String originFileAbsolutePath = renameFileDto.getOriginFileAbsolutePath(); + String targetFileAbsolutePath = renameFileDto.getTargetFileAbsolutePath(); + storageOperator.copy(originFileAbsolutePath, targetFileAbsolutePath, true, true); + log.info("Success rename file: {} -> {} ", originFileAbsolutePath, targetFileAbsolutePath); + } - result = verifyFile(name, type, file); - if (!result.getCode().equals(Status.SUCCESS.getCode())) { - return result; - } + @Override + public void updateFile(UpdateFileRequest updateFileRequest) { + UpdateFileDto updateFileDto = updateFileRequestTransformer.transform(updateFileRequest); + updateFileDtoValidator.validate(updateFileDto); - Date now = new Date(); - - resource.setAlias(name); - resource.setFileName(name); - resource.setFullName(fullName); - resource.setUpdateTime(now); - if (file != null) { - resource.setSize(file.getSize()); - } - - // if name unchanged, return directly without moving on HDFS - if (originResourceName.equals(name) && file == null) { - return result; - } - - if (file != null) { - // fail upload - if (!upload(loginUser, fullName, file, type)) { - log.error("Storage operation error, resourceName:{}, originFileName:{}.", name, - RegexUtils.escapeNRT(file.getOriginalFilename())); - putMsg(result, Status.HDFS_OPERATION_ERROR); - throw new ServiceException( - String.format("upload resource: %s file: %s failed.", name, file.getOriginalFilename())); - } - if (!fullName.equals(originFullName)) { - try { - storageOperate.delete(originFullName, false); - } catch (IOException e) { - log.error("Resource delete error, resourceFullName:{}.", originFullName, e); - throw new ServiceException(String.format("delete resource: %s failed.", originFullName)); - } - } - - ApiServerMetrics.recordApiResourceUploadSize(file.getSize()); - return result; - } - - // get the path of dest file in hdfs - String destHdfsFileName = fullName; + String srcLocalTmpFileAbsolutePath = copyFileToLocal(updateFileDto.getFile()); try { - log.info("start copy {} -> {}", originFullName, destHdfsFileName); - storageOperate.copy(originFullName, destHdfsFileName, true, true); - putMsg(result, Status.SUCCESS); + storageOperator.upload(srcLocalTmpFileAbsolutePath, updateFileDto.getFileAbsolutePath(), true, true); + ApiServerMetrics.recordApiResourceUploadSize(updateFileDto.getFile().getSize()); + log.info("Success upload resource file: {} complete.", updateFileDto.getFileAbsolutePath()); + } catch (Exception ex) { + // If exception, clear the tmp path + FileUtils.deleteFile(srcLocalTmpFileAbsolutePath); + throw ex; + } + } + + @Override + public PageInfo pagingResourceItem(PagingResourceItemRequest pagingResourceItemRequest) { + + QueryResourceDto queryResourceDto = pagingResourceItemRequestTransformer.transform(pagingResourceItemRequest); + List resourceAbsolutePaths = queryResourceDto.getResourceAbsolutePaths(); + if (CollectionUtils.isEmpty(resourceAbsolutePaths)) { + return new PageInfo<>(pagingResourceItemRequest.getPageNo(), pagingResourceItemRequest.getPageSize()); + } + + for (String resourceAbsolutePath : resourceAbsolutePaths) { + createDirectoryDtoValidator.exceptionResourceAbsolutePathInvalidated(resourceAbsolutePath); + createDirectoryDtoValidator.exceptionUserNoResourcePermission(pagingResourceItemRequest.getLoginUser(), + resourceAbsolutePath); + } + + Integer pageNo = pagingResourceItemRequest.getPageNo(); + Integer pageSize = pagingResourceItemRequest.getPageSize(); + + List storageEntities = resourceAbsolutePaths.stream() + .flatMap(resourceAbsolutePath -> storageOperator.listStorageEntity(resourceAbsolutePath).stream()) + .collect(Collectors.toList()); + + List result = storageEntities + .stream() + .filter(storageEntity -> storageEntity.getFileName() + .contains(pagingResourceItemRequest.getResourceNameKeyWord())) + .skip((long) (pageNo - 1) * pageSize) + .limit(pageSize) + .map(ResourceItemVO::new) + .collect(Collectors.toList()); + + return PageInfo.builder() + .pageNo(pagingResourceItemRequest.getPageNo()) + .pageSize(pagingResourceItemRequest.getPageSize()) + .total(storageEntities.size()) + .totalList(result) + .build(); + } + + @Override + public List queryResourceFiles(User loginUser, ResourceType resourceType) { + Tenant tenant = tenantDao.queryOptionalById(loginUser.getTenantId()) + .orElseThrow(() -> new ServiceException(Status.TENANT_NOT_EXIST, loginUser.getTenantId())); + String storageBaseDirectory = storageOperator.getStorageBaseDirectory(tenant.getTenantCode(), resourceType); + List allResourceFiles = storageOperator.listFileStorageEntityRecursively(storageBaseDirectory); + + Visitor visitor = new ResourceTreeVisitor(allResourceFiles); + return visitor.visit("").getChildren(); + } + + @Override + public void delete(DeleteResourceRequest deleteResourceRequest) { + DeleteResourceDto deleteResourceDto = DeleteResourceDto.builder() + .loginUser(deleteResourceRequest.getLoginUser()) + .resourceAbsolutePath(deleteResourceRequest.getResourceAbsolutePath()) + .build(); + deleteResourceDtoValidator.validate(deleteResourceDto); + storageOperator.delete(deleteResourceDto.getResourceAbsolutePath(), true); + } + + @Override + public FetchFileContentResponse fetchResourceFileContent(FetchFileContentRequest fetchFileContentRequest) { + FetchFileContentDto fetchFileContentDto = FetchFileContentDto.builder() + .loginUser(fetchFileContentRequest.getLoginUser()) + .resourceFileAbsolutePath(fetchFileContentRequest.getResourceFileAbsolutePath()) + .skipLineNum(fetchFileContentRequest.getSkipLineNum()) + .limit(fetchFileContentRequest.getLimit()) + .build(); + fetchFileContentDtoValidator.validate(fetchFileContentDto); + + String content = storageOperator + .fetchFileContent( + fetchFileContentRequest.getResourceFileAbsolutePath(), + fetchFileContentRequest.getSkipLineNum(), + fetchFileContentRequest.getLimit()) + .stream() + .collect(Collectors.joining("\n")); + + ApiServerMetrics.recordApiResourceDownloadSize(content.length()); + + return FetchFileContentResponse.builder() + .content(content) + .build(); + } + + @Override + public void updateFileFromContent(UpdateFileFromContentRequest updateFileContentRequest) { + UpdateFileFromContentDto updateFileFromContentDto = + updateFileFromContentRequestTransformer.transform(updateFileContentRequest); + updateFileFromContentDtoValidator.validate(updateFileFromContentDto); + + String srcLocalTmpFileAbsolutePath = copyFileToLocal(updateFileFromContentDto.getFileContent()); + try { + storageOperator.upload(srcLocalTmpFileAbsolutePath, updateFileFromContentDto.getFileAbsolutePath(), true, + true); + ApiServerMetrics.recordApiResourceUploadSize(updateFileFromContentDto.getFileContent().length()); + log.info("Success upload resource file: {} complete.", updateFileFromContentDto.getFileAbsolutePath()); + } catch (Exception ex) { + // If exception, clear the tmp path + FileUtils.deleteFile(srcLocalTmpFileAbsolutePath); + throw new ServiceException("Update the resource file from content: " + + updateFileFromContentDto.getFileAbsolutePath() + " failed", ex); + } + } + + @Override + public void downloadResource(HttpServletResponse response, DownloadFileRequest downloadFileRequest) { + DownloadFileDto downloadFileDto = DownloadFileDto.builder() + .loginUser(downloadFileRequest.getLoginUser()) + .fileAbsolutePath(downloadFileRequest.getFileAbsolutePath()) + .build(); + downloadFileDtoValidator.validate(downloadFileDto); + + String fileName = new File(downloadFileDto.getFileAbsolutePath()).getName(); + String localTmpFileAbsolutePath = FileUtils.getDownloadFilename(fileName); + + try { + storageOperator.download(downloadFileRequest.getFileAbsolutePath(), localTmpFileAbsolutePath, true); + int length = (int) new File(localTmpFileAbsolutePath).length(); + ApiServerMetrics.recordApiResourceDownloadSize(length); + + response.reset(); + response.setContentType("application/octet-stream"); + response.setCharacterEncoding("utf-8"); + response.setContentLength(length); + response.setHeader("Content-Disposition", "attachment;filename=" + fileName); + Files.copy(Paths.get(localTmpFileAbsolutePath), response.getOutputStream()); } catch (Exception e) { - log.error(MessageFormat.format(" copy {0} -> {1} fail", originFullName, destHdfsFileName), e); - putMsg(result, Status.HDFS_COPY_FAIL); throw new ServiceException( - MessageFormat.format(Status.HDFS_COPY_FAIL.getMsg(), originFullName, destHdfsFileName)); - } - - return result; - } - - private Result verifyFile(String name, ResourceType type, MultipartFile file) { - Result result = new Result<>(); - putMsg(result, Status.SUCCESS); - - if (FileUtils.directoryTraversal(name)) { - log.warn("Parameter file alias name verify failed, fileAliasName:{}.", RegexUtils.escapeNRT(name)); - putMsg(result, Status.VERIFY_PARAMETER_NAME_FAILED); - return result; - } - - if (file != null && FileUtils.directoryTraversal(Objects.requireNonNull(file.getOriginalFilename()))) { - log.warn("File original name verify failed, fileOriginalName:{}.", - RegexUtils.escapeNRT(file.getOriginalFilename())); - putMsg(result, Status.VERIFY_PARAMETER_NAME_FAILED); - return result; - } - - if (file != null) { - // file is empty - if (file.isEmpty()) { - log.warn("Parameter file is empty, fileOriginalName:{}.", - RegexUtils.escapeNRT(file.getOriginalFilename())); - putMsg(result, Status.RESOURCE_FILE_IS_EMPTY); - return result; - } - - // file suffix - String fileSuffix = Files.getFileExtension(file.getOriginalFilename()); - String nameSuffix = Files.getFileExtension(name); - - // determine file suffix - if (!fileSuffix.equalsIgnoreCase(nameSuffix)) { - // rename file suffix and original suffix must be consistent - log.warn("Rename file suffix and original suffix must be consistent, fileOriginalName:{}.", - RegexUtils.escapeNRT(file.getOriginalFilename())); - putMsg(result, Status.RESOURCE_SUFFIX_FORBID_CHANGE); - return result; - } - - // If resource type is UDF, only jar packages are allowed to be uploaded, and the suffix must be .jar - if (Constants.UDF.equals(type.name()) && !JAR.equalsIgnoreCase(fileSuffix)) { - log.warn(Status.UDF_RESOURCE_SUFFIX_NOT_JAR.getMsg()); - putMsg(result, Status.UDF_RESOURCE_SUFFIX_NOT_JAR); - return result; - } - if (file.getSize() > Constants.MAX_FILE_SIZE) { - log.warn( - "Resource file size is larger than max file size, fileOriginalName:{}, fileSize:{}, maxFileSize:{}.", - RegexUtils.escapeNRT(file.getOriginalFilename()), file.getSize(), Constants.MAX_FILE_SIZE); - putMsg(result, Status.RESOURCE_SIZE_EXCEED_LIMIT); - return result; - } - } - return result; - } - - /** - * query resources list paging - * - * @param loginUser login user - * @param fullName resource full name - * @param resTenantCode tenantCode in the request field "resTenantCode" for tenant code owning the resource, - * can be different from the login user in the case of logging in as admin users. - * @param type resource type - * @param searchVal search value - * @param pageNo page number - * @param pageSize page size - * @return resource list page - */ - @Override - public Result> queryResourceListPaging(User loginUser, String fullName, - String resTenantCode, ResourceType type, - String searchVal, Integer pageNo, Integer pageSize) { - Result> result = new Result<>(); - PageInfo pageInfo = new PageInfo<>(pageNo, pageSize); - if (storageOperate == null) { - log.warn("The resource storage is not opened."); - return Result.success(pageInfo); - } - - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, fullName); - - if (!isUserTenantValid(isAdmin(loginUser), tenantCode, resTenantCode)) { - log.error("current user does not have permission"); - putMsg(result, Status.NO_CURRENT_OPERATING_PERMISSION); - return result; - } - - List resourcesList; - try { - resourcesList = queryStorageEntityList(loginUser, fullName, type, tenantCode, false); - } catch (ServiceException e) { - putMsg(result, Status.RESOURCE_NOT_EXIST); - return result; - } - - // remove leading and trailing spaces in searchVal - String trimmedSearchVal = searchVal != null ? searchVal.trim() : ""; - // filter based on trimmed searchVal - List filteredResourceList = resourcesList.stream() - .filter(x -> x.getFileName().contains(trimmedSearchVal)).collect(Collectors.toList()); - // inefficient pagination - List slicedResourcesList = filteredResourceList.stream().skip((long) (pageNo - 1) * pageSize) - .limit(pageSize).collect(Collectors.toList()); - - pageInfo.setTotal(filteredResourceList.size()); - pageInfo.setTotalList(slicedResourcesList); - result.setData(pageInfo); - putMsg(result, Status.SUCCESS); - return result; - } - - private List queryStorageEntityList(User loginUser, String fullName, ResourceType type, - String tenantCode, boolean recursive) { - String defaultPath = ""; - List resourcesList = new ArrayList<>(); - String resourceStorageType = - PropertyUtils.getString(Constants.RESOURCE_STORAGE_TYPE, ResUploadType.LOCAL.name()); - if (isAdmin(loginUser) && StringUtils.isBlank(fullName)) { - // list all tenants' resources to admin users in the root directory - List userList = userMapper.selectList(null); - Set visitedTenantEntityCode = new HashSet<>(); - for (User userEntity : userList) { - String tenantEntityCode = getTenantCode(userEntity); - if (!visitedTenantEntityCode.contains(tenantEntityCode)) { - defaultPath = storageOperate.getResDir(tenantEntityCode); - if (type.equals(ResourceType.UDF)) { - defaultPath = storageOperate.getUdfDir(tenantEntityCode); - } - try { - resourcesList.addAll(recursive - ? storageOperate.listFilesStatusRecursively(defaultPath, defaultPath, tenantEntityCode, - type) - : storageOperate.listFilesStatus(defaultPath, defaultPath, tenantEntityCode, type)); - - visitedTenantEntityCode.add(tenantEntityCode); - } catch (Exception e) { - log.error(e.getMessage() + " Resource path: {}", defaultPath, e); - throw new ServiceException( - String.format(e.getMessage() + " make sure resource path: %s exists in %s", defaultPath, - resourceStorageType)); - } - } - } - } else { - defaultPath = storageOperate.getResDir(tenantCode); - if (type.equals(ResourceType.UDF)) { - defaultPath = storageOperate.getUdfDir(tenantCode); - } - - try { - if (StringUtils.isBlank(fullName)) { - fullName = defaultPath; - } - resourcesList = - recursive ? storageOperate.listFilesStatusRecursively(fullName, defaultPath, tenantCode, type) - : storageOperate.listFilesStatus(fullName, defaultPath, tenantCode, type); - } catch (Exception e) { - log.error(e.getMessage() + " Resource path: {}", fullName, e); - throw new ServiceException(String.format(e.getMessage() + " make sure resource path: %s exists in %s", - defaultPath, resourceStorageType)); - } - } - - return resourcesList; - } - - /** - * create directory - * xxx The steps to verify resources are cumbersome and can be optimized - * - * @param loginUser login user - * @param fullName full name - * @param type resource type - * @param result Result - */ - private void createDirectory(User loginUser, String fullName, ResourceType type, Result result) { - String tenantCode = tenantMapper.queryById(loginUser.getTenantId()).getTenantCode(); - // String directoryName = storageOperate.getFileName(type, tenantCode, fullName); - String resourceRootPath = storageOperate.getDir(type, tenantCode); - try { - if (!storageOperate.exists(resourceRootPath)) { - storageOperate.createTenantDirIfNotExists(tenantCode); - } - - if (!storageOperate.mkdir(tenantCode, fullName)) { - throw new ServiceException(String.format("Create resource directory: %s failed.", fullName)); - } - putMsg(result, Status.SUCCESS); - } catch (Exception e) { - throw new ServiceException(String.format("create resource directory: %s failed.", fullName)); - } - } - - /** - * upload file to hdfs - * - * @param loginUser login user - * @param fullName full name - * @param file file - * @param type resource type - * @return upload success return true, otherwise false - */ - private boolean upload(User loginUser, String fullName, MultipartFile file, ResourceType type) { - // save to local - String fileSuffix = Files.getFileExtension(file.getOriginalFilename()); - String nameSuffix = Files.getFileExtension(fullName); - - // determine file suffix - if (!fileSuffix.equalsIgnoreCase(nameSuffix)) { - return false; - } - // query tenant - String tenantCode = getTenantCode(loginUser); - // random file name - String localFilename = FileUtils.getUploadFilename(tenantCode, UUID.randomUUID().toString()); - - // save file to hdfs, and delete original file - String resourcePath = storageOperate.getDir(type, tenantCode); - try { - // if tenant dir not exists - if (!storageOperate.exists(resourcePath)) { - storageOperate.createTenantDirIfNotExists(tenantCode); - } - org.apache.dolphinscheduler.api.utils.FileUtils.copyInputStreamToFile(file, localFilename); - storageOperate.upload(tenantCode, localFilename, fullName, true, true); - FileUtils.deleteFile(localFilename); - } catch (Exception e) { - FileUtils.deleteFile(localFilename); - log.error(e.getMessage(), e); - return false; - } - return true; - } - - /** - * query resource list - * - * @param loginUser login user - * @param type resource type - * @param fullName resource full name - * @return resource list - */ - @Override - public Map queryResourceList(User loginUser, ResourceType type, String fullName) { - Map result = new HashMap<>(); - if (storageOperate == null) { - result.put(Constants.DATA_LIST, Collections.emptyList()); - result.put(Constants.STATUS, Status.SUCCESS); - return result; - } - - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return null; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, fullName); - - String baseDir = storageOperate.getDir(type, tenantCode); - - List resourcesList = new ArrayList<>(); - if (StringUtils.isBlank(fullName)) { - if (isAdmin(loginUser)) { - List userList = userMapper.selectList(null); - Set visitedTenantEntityCode = new HashSet<>(); - for (User userEntity : userList) { - String tenantEntityCode = getTenantCode(userEntity); - if (!visitedTenantEntityCode.contains(tenantEntityCode)) { - baseDir = storageOperate.getDir(type, tenantEntityCode); - resourcesList.addAll(storageOperate.listFilesStatusRecursively(baseDir, baseDir, - tenantEntityCode, type)); - visitedTenantEntityCode.add(tenantEntityCode); - } - } - } else { - resourcesList = storageOperate.listFilesStatusRecursively(baseDir, baseDir, tenantCode, type); - } - } else { - resourcesList = storageOperate.listFilesStatusRecursively(fullName, baseDir, tenantCode, type); - } - - Visitor resourceTreeVisitor = new ResourceTreeVisitor(resourcesList); - result.put(Constants.DATA_LIST, resourceTreeVisitor.visit(baseDir).getChildren()); - putMsg(result, Status.SUCCESS); - - return result; - } - - /** - * query resource list by program type - * - * @param loginUser login user - * @param type resource type - * @return resource list - */ - @Override - public Result queryResourceByProgramType(User loginUser, ResourceType type, ProgramType programType) { - Result result = new Result<>(); - - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - Tenant tenant = tenantMapper.queryById(user.getTenantId()); - if (tenant == null) { - log.error("tenant not exists"); - putMsg(result, Status.CURRENT_LOGIN_USER_TENANT_NOT_EXIST); - return result; - } - - String tenantCode = tenant.getTenantCode(); - - List allResourceList = queryStorageEntityList(loginUser, "", type, tenantCode, true); - - String suffix = ".jar"; - if (programType != null) { - switch (programType) { - case JAVA: - case SCALA: - break; - case PYTHON: - suffix = ".py"; - break; - default: - } - } - List resources = new ResourceFilter(suffix, new ArrayList<>(allResourceList)).filter(); - Visitor visitor = new ResourceTreeVisitor(resources); - result.setData(visitor.visit("").getChildren()); - putMsg(result, Status.SUCCESS); - return result; - } - - /** - * delete resource - * - * @param loginUser login user - * @param fullName resource full name - * @param resTenantCode tenantCode in the request field "resTenantCode" for tenant code owning the resource, - * can be different from the login user in the case of logging in as admin users. - * @return delete result code - * @throws IOException exception - */ - @Override - @Transactional(rollbackFor = Exception.class) - public Result delete(User loginUser, String fullName, String resTenantCode) throws IOException { - Result result = new Result<>(); - - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, fullName); - - if (!isUserTenantValid(isAdmin(loginUser), tenantCode, resTenantCode)) { - log.error("current user does not have permission"); - putMsg(result, Status.NO_CURRENT_OPERATING_PERMISSION); - return result; - } - - String baseDir = storageOperate.getResDir(tenantCode); - - StorageEntity resource; - try { - resource = storageOperate.getFileStatus(fullName, baseDir, resTenantCode, null); - } catch (Exception e) { - log.error(e.getMessage() + " Resource path: {}", fullName, e); - putMsg(result, Status.RESOURCE_NOT_EXIST); - throw new ServiceException(String.format(e.getMessage() + " Resource path: %s", fullName)); - } - - if (resource == null) { - log.error("Resource does not exist, resource full name:{}.", fullName); - putMsg(result, Status.RESOURCE_NOT_EXIST); - return result; - } - - // recursively delete a folder - List allChildren = - storageOperate.listFilesStatusRecursively(fullName, baseDir, resTenantCode, resource.getType()) - .stream().map(storageEntity -> storageEntity.getFullName()).collect(Collectors.toList()); - - String[] allChildrenFullNameArray = allChildren.stream().toArray(String[]::new); - - // if resource type is UDF,need check whether it is bound by UDF function - if (resource.getType() == (ResourceType.UDF)) { - List udfFuncs = udfFunctionMapper.listUdfByResourceFullName(allChildrenFullNameArray); - if (CollectionUtils.isNotEmpty(udfFuncs)) { - log.warn("Resource can not be deleted because it is bound by UDF functions, udfFuncIds:{}", udfFuncs); - putMsg(result, Status.UDF_RESOURCE_IS_BOUND, udfFuncs.get(0).getFuncName()); - return result; - } - } - - // delete file on hdfs,S3 - storageOperate.delete(fullName, allChildren, true); - - putMsg(result, Status.SUCCESS); - - return result; - } - - /** - * verify resource by name and type - * - * @param loginUser login user - * @param fullName resource full name - * @param type resource type - * @return true if the resource name not exists, otherwise return false - */ - @Override - public Result verifyResourceName(String fullName, ResourceType type, User loginUser) { - Result result = new Result<>(); - putMsg(result, Status.SUCCESS); - if (checkResourceExists(fullName)) { - log.error("Resource with same name exists so can not create again, resourceType:{}, resourceName:{}.", type, - RegexUtils.escapeNRT(fullName)); - putMsg(result, Status.RESOURCE_EXIST); - } - - return result; - } - - /** - * verify resource by full name or pid and type - * - * @param fileName resource file name - * @param type resource type - * @param resTenantCode tenantCode in the request field "resTenantCode" for tenant code owning the resource, - * can be different from the login user in the case of logging in as admin users. - * @return true if the resource full name or pid not exists, otherwise return false - */ - @Override - public Result queryResourceByFileName(User loginUser, String fileName, ResourceType type, - String resTenantCode) { - Result result = new Result<>(); - if (StringUtils.isBlank(fileName)) { - putMsg(result, Status.REQUEST_PARAMS_NOT_VALID_ERROR); - return result; - } - - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - - if (!isUserTenantValid(isAdmin(loginUser), tenantCode, resTenantCode)) { - log.error("current user does not have permission"); - putMsg(result, Status.NO_CURRENT_OPERATING_PERMISSION); - return result; - } - - String defaultPath = storageOperate.getDir(type, resTenantCode); - StorageEntity file; - try { - file = storageOperate.getFileStatus(defaultPath + fileName, defaultPath, resTenantCode, type); - } catch (Exception e) { - log.error(e.getMessage() + " Resource path: {}", defaultPath + fileName, e); - putMsg(result, Status.RESOURCE_NOT_EXIST); - return result; - } - - putMsg(result, Status.SUCCESS); - result.setData(file); - return result; - } - - /** - * view resource file online - * - * @param fullName resource fullName - * @param resTenantCode owner's tenant code of the resource - * @param skipLineNum skip line number - * @param limit limit - * @return resource content - */ - @Override - public Result readResource(User loginUser, String fullName, String resTenantCode, int skipLineNum, - int limit) { - Result result = new Result<>(); - - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, fullName); - - if (!isUserTenantValid(isAdmin(loginUser), tenantCode, resTenantCode)) { - log.error("current user does not have permission"); - putMsg(result, Status.NO_CURRENT_OPERATING_PERMISSION); - return result; - } - - // check preview or not by file suffix - String nameSuffix = Files.getFileExtension(fullName); - String resourceViewSuffixes = FileUtils.getResourceViewSuffixes(); - if (StringUtils.isNotEmpty(resourceViewSuffixes)) { - List strList = Arrays.asList(resourceViewSuffixes.split(",")); - if (!strList.contains(nameSuffix)) { - log.error("Resource suffix does not support view,resourceFullName:{}, suffix:{}.", fullName, - nameSuffix); - putMsg(result, Status.RESOURCE_SUFFIX_NOT_SUPPORT_VIEW); - return result; - } - } - - List content; - try { - if (storageOperate.exists(fullName)) { - content = storageOperate.vimFile(tenantCode, fullName, skipLineNum, limit); - long size = content.stream().mapToLong(String::length).sum(); - ApiServerMetrics.recordApiResourceDownloadSize(size); - } else { - log.error("read file {} not exist in storage", fullName); - putMsg(result, Status.RESOURCE_FILE_NOT_EXIST, fullName); - return result; - } - - } catch (Exception e) { - log.error("Resource {} read failed", fullName, e); - putMsg(result, Status.HDFS_OPERATION_ERROR); - return result; - } - - putMsg(result, Status.SUCCESS); - Map map = new HashMap<>(); - map.put(ALIAS, fullName); - map.put(CONTENT, String.join("\n", content)); - result.setData(map); - - return result; - } - - /** - * create resource file online - * - * @param loginUser login user - * @param type resource type - * @param fileName file name - * @param fileSuffix file suffix - * @param content content - * @param currentDir current directory - * @return create result code - */ - @Override - @Transactional - public Result createResourceFile(User loginUser, ResourceType type, String fileName, String fileSuffix, - String content, String currentDir) { - Result result = new Result<>(); - - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, currentDir); - - if (FileUtils.directoryTraversal(fileName)) { - log.warn("File name verify failed, fileName:{}.", RegexUtils.escapeNRT(fileName)); - putMsg(result, Status.VERIFY_PARAMETER_NAME_FAILED); - return result; - } - - // check file suffix - String nameSuffix = fileSuffix.trim(); - String resourceViewSuffixes = FileUtils.getResourceViewSuffixes(); - if (StringUtils.isNotEmpty(resourceViewSuffixes)) { - List strList = Arrays.asList(resourceViewSuffixes.split(",")); - if (!strList.contains(nameSuffix)) { - log.warn("Resource suffix does not support view, suffix:{}.", nameSuffix); - putMsg(result, Status.RESOURCE_SUFFIX_NOT_SUPPORT_VIEW); - return result; - } - } - - String name = fileName.trim() + "." + nameSuffix; - - String userResRootPath = storageOperate.getResDir(tenantCode); - String fullName = currentDir.contains(userResRootPath) ? currentDir + name : userResRootPath + name; - - result = verifyResourceName(fullName, type, loginUser); - if (!result.getCode().equals(Status.SUCCESS.getCode())) { - return result; - } - - result = uploadContentToStorage(fullName, tenantCode, content); - if (!result.getCode().equals(Status.SUCCESS.getCode())) { - throw new ServiceException(result.getMsg()); - } - return result; - } - - @Override - @Transactional - public StorageEntity createOrUpdateResource(String userName, String filepath, - String resourceContent) throws Exception { - User user = userMapper.queryByUserNameAccurately(userName); - int suffixLabelIndex = filepath.indexOf(PERIOD); - if (suffixLabelIndex == -1) { - throw new IllegalArgumentException(String - .format("Not allow create or update resources without extension name, filepath: %s", filepath)); - } - - String defaultPath = storageOperate.getResDir(user.getTenantCode()); - String fullName = defaultPath + filepath; - - Result result = uploadContentToStorage(fullName, user.getTenantCode(), resourceContent); - if (result.getCode() != Status.SUCCESS.getCode()) { - throw new ServiceException(result.getMsg()); - } - return storageOperate.getFileStatus(fullName, defaultPath, user.getTenantCode(), ResourceType.FILE); - } - - /** - * updateProcessInstance resource - * - * @param fullName resource full name - * @param resTenantCode tenantCode in the request field "resTenantCode" for tenant code owning the resource, - * can be different from the login user in the case of logging in as admin users. - * @param content content - * @return update result cod - */ - @Override - @Transactional - public Result updateResourceContent(User loginUser, String fullName, String resTenantCode, String content) { - Result result = new Result<>(); - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, fullName); - - if (!isUserTenantValid(isAdmin(loginUser), tenantCode, resTenantCode)) { - log.error("current user does not have permission"); - putMsg(result, Status.NO_CURRENT_OPERATING_PERMISSION); - return result; - } - - StorageEntity resource; - try { - resource = storageOperate.getFileStatus(fullName, "", resTenantCode, ResourceType.FILE); - } catch (Exception e) { - log.error("error occurred when fetching resource information , resource full name {}", fullName); - putMsg(result, Status.RESOURCE_NOT_EXIST); - return result; - } - - if (resource == null) { - log.error("Resource does not exist, resource full name:{}.", fullName); - putMsg(result, Status.RESOURCE_NOT_EXIST); - return result; - } - - // check can edit by file suffix - String nameSuffix = Files.getFileExtension(resource.getAlias()); - String resourceViewSuffixes = FileUtils.getResourceViewSuffixes(); - if (StringUtils.isNotEmpty(resourceViewSuffixes)) { - List strList = Arrays.asList(resourceViewSuffixes.split(",")); - if (!strList.contains(nameSuffix)) { - log.warn("Resource suffix does not support view, resource full name:{}, suffix:{}.", fullName, - nameSuffix); - putMsg(result, Status.RESOURCE_SUFFIX_NOT_SUPPORT_VIEW); - return result; - } - } - - result = uploadContentToStorage(resource.getFullName(), resTenantCode, content); - - if (!result.getCode().equals(Status.SUCCESS.getCode())) { - throw new ServiceException(result.getMsg()); - } else - log.info("Update resource content complete, resource full name:{}.", fullName); - return result; - } - - /** - * @param fullName resource full name - * @param tenantCode tenant code - * @param content content - * @return result - */ - private Result uploadContentToStorage(String fullName, String tenantCode, String content) { - Result result = new Result<>(); - String localFilename = ""; - try { - localFilename = FileUtils.getUploadFilename(tenantCode, UUID.randomUUID().toString()); - - if (!FileUtils.writeContent2File(content, localFilename)) { - // write file fail - log.error("Write file error, fileName:{}, content:{}.", localFilename, RegexUtils.escapeNRT(content)); - putMsg(result, Status.RESOURCE_NOT_EXIST); - return result; - } - - // get resource file path - String resourcePath = storageOperate.getResDir(tenantCode); - log.info("resource path is {}, resource dir is {}", fullName, resourcePath); - - if (!storageOperate.exists(resourcePath)) { - // create if tenant dir not exists - storageOperate.createTenantDirIfNotExists(tenantCode); - log.info("Create tenant dir because path {} does not exist, tenantCode:{}.", resourcePath, tenantCode); - } - if (storageOperate.exists(fullName)) { - storageOperate.delete(fullName, false); - } - - storageOperate.upload(tenantCode, localFilename, fullName, true, true); - } catch (Exception e) { - log.error("Upload content to storage error, tenantCode:{}, destFileName:{}.", tenantCode, localFilename, e); - result.setCode(Status.HDFS_OPERATION_ERROR.getCode()); - result.setMsg(String.format("copy %s to hdfs %s fail", localFilename, fullName)); - return result; + "Download the resource file: " + downloadFileRequest.getFileAbsolutePath() + " failed", e); } finally { - FileUtils.deleteFile(localFilename); - } - log.info("Upload content to storage complete, tenantCode:{}, destFileName:{}.", tenantCode, localFilename); - putMsg(result, Status.SUCCESS); - return result; - } - - /** - * download file - * - * @return resource content - */ - @Override - public org.springframework.core.io.Resource downloadResource(User loginUser, String fullName) { - if (fullName.endsWith("/")) { - log.error("resource id {} is directory,can't download it", fullName); - throw new ServiceException("can't download directory"); - } - - int userId = loginUser.getId(); - User user = userMapper.selectById(userId); - if (user == null) { - log.error("User does not exits, userId:{}.", userId); - throw new ServiceException(String.format("Resource owner id %d does not exist", userId)); - } - - String tenantCode = getTenantCode(user); - checkFullName(tenantCode, fullName); - - String[] aliasArr = fullName.split("/"); - String alias = aliasArr[aliasArr.length - 1]; - String localFileName = FileUtils.getDownloadFilename(alias); - log.info("Resource path is {}, download local filename is {}", alias, localFileName); - - try { - storageOperate.download(fullName, localFileName, true); - ApiServerMetrics.recordApiResourceDownloadSize(java.nio.file.Files.size(Paths.get(localFileName))); - return org.apache.dolphinscheduler.api.utils.FileUtils.file2Resource(localFileName); - } catch (IOException e) { - log.error("Download resource error, the path is {}, and local filename is {}, the error message is {}", - fullName, localFileName, e.getMessage()); - throw new ServiceException("Download the resource file failed ,it may be related to your storage"); + FileUtils.deleteFile(localTmpFileAbsolutePath); } } @Override - public StorageEntity queryFileStatus(String userName, String fileName) throws Exception { - // TODO: It is used in PythonGateway, should be revised - User user = userMapper.queryByUserNameAccurately(userName); - - String defaultPath = storageOperate.getResDir(user.getTenantCode()); - return storageOperate.getFileStatus(defaultPath + fileName, defaultPath, user.getTenantCode(), - ResourceType.FILE); + public StorageEntity queryFileStatus(String userName, String fileAbsolutePath) { + return storageOperator.getStorageEntity(fileAbsolutePath); } @Override - public DeleteDataTransferResponse deleteDataTransferData(User loginUser, Integer days) { - DeleteDataTransferResponse result = new DeleteDataTransferResponse(); + public String queryResourceBaseDir(User loginUser, ResourceType type) { User user = userMapper.selectById(loginUser.getId()); if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; + throw new ServiceException(Status.USER_NOT_EXIST); } - String tenantCode = getTenantCode(user); - - String baseFolder = storageOperate.getResourceFullName(tenantCode, "DATA_TRANSFER"); - - LocalDateTime now = LocalDateTime.now(); - now = now.minus(days, ChronoUnit.DAYS); - String deleteDate = now.toLocalDate().toString().replace("-", ""); - List storageEntities; - try { - storageEntities = new ArrayList<>( - storageOperate.listFilesStatus(baseFolder, baseFolder, tenantCode, ResourceType.FILE)); - } catch (Exception e) { - log.error("delete data transfer data error", e); - putMsg(result, Status.DELETE_RESOURCE_ERROR); - return result; - } - - List successList = new ArrayList<>(); - List failList = new ArrayList<>(); - - for (StorageEntity storageEntity : storageEntities) { - File path = new File(storageEntity.getFullName()); - String date = path.getName(); - if (date.compareTo(deleteDate) <= 0) { - try { - storageOperate.delete(storageEntity.getFullName(), true); - successList.add(storageEntity.getFullName()); - } catch (Exception ex) { - log.error("delete data transfer data {} error, please delete it manually", date, ex); - failList.add(storageEntity.getFullName()); - } - } - } - - result.setSuccessList(successList); - result.setFailedList(failList); - putMsg(result, Status.SUCCESS); - return result; + Tenant tenant = tenantDao.queryOptionalById(user.getTenantId()) + .orElseThrow(() -> new ServiceException(Status.CURRENT_LOGIN_USER_TENANT_NOT_EXIST)); + return storageOperator.getStorageBaseDirectory(tenant.getTenantCode(), type); } - /** - * get resource base dir - * - * @param loginUser login user - * @param type resource type - * @return - */ - @Override - public Result queryResourceBaseDir(User loginUser, ResourceType type) { - Result result = new Result<>(); - if (storageOperate == null) { - putMsg(result, Status.SUCCESS); - result.setData(EMPTY_STRING); - return result; - } - User user = userMapper.selectById(loginUser.getId()); - if (user == null) { - log.error("user {} not exists", loginUser.getId()); - putMsg(result, Status.USER_NOT_EXIST, loginUser.getId()); - return result; - } - - String tenantCode = getTenantCode(user); - - String baseDir = isAdmin(loginUser) ? storageOperate.getDir(ResourceType.ALL, tenantCode) - : storageOperate.getDir(type, tenantCode); - - putMsg(result, Status.SUCCESS); - result.setData(baseDir); - - return result; + // Copy the file to the local file system and return the local file absolute path + @SneakyThrows + private String copyFileToLocal(MultipartFile multipartFile) { + String localTmpFileAbsolutePath = FileUtils.getUploadFileLocalTmpAbsolutePath(); + FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), localTmpFileAbsolutePath); + return localTmpFileAbsolutePath; } - /** - * check permission by comparing login user's tenantCode with tenantCode in the request - * - * @param isAdmin is the login user admin - * @param userTenantCode loginUser's tenantCode - * @param resTenantCode tenantCode in the request field "resTenantCode" for tenant code owning the resource, - * can be different from the login user in the case of logging in as admin users. - * @return isValid - */ - private boolean isUserTenantValid(boolean isAdmin, String userTenantCode, - String resTenantCode) throws ServiceException { - if (isAdmin) { - return true; - } - if (StringUtils.isEmpty(resTenantCode)) { - // TODO: resource tenant code will be empty when query resources list, need to be optimized - return true; - } - return resTenantCode.equals(userTenantCode); + // Copy the file to the local file system and return the local file absolute path + private String copyFileToLocal(String fileContent) { + String localTmpFileAbsolutePath = FileUtils.getUploadFileLocalTmpAbsolutePath(); + FileUtils.writeContent2File(fileContent, localTmpFileAbsolutePath); + return localTmpFileAbsolutePath; } - private String getTenantCode(User user) { - Tenant tenant = tenantMapper.queryById(user.getTenantId()); - if (tenant == null) { - throw new ServiceException(Status.CURRENT_LOGIN_USER_TENANT_NOT_EXIST); - } - return tenant.getTenantCode(); - } - - private void checkFullName(String userTenantCode, String fullName) { - if (StringUtils.isEmpty(fullName)) { - return; - } - if (FOLDER_SEPARATOR.equalsIgnoreCase(fullName)) { - return; - } - // Avoid returning to the parent directory - if (fullName.contains("../")) { - throw new ServiceException(Status.ILLEGAL_RESOURCE_PATH, fullName); - } - String baseDir = storageOperate.getDir(ResourceType.ALL, userTenantCode); - if (!StringUtils.startsWith(fullName, baseDir)) { - throw new ServiceException(Status.ILLEGAL_RESOURCE_PATH, fullName); - } - } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TaskDefinitionServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TaskDefinitionServiceImpl.java index 2887606a1d..ae3410cb77 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TaskDefinitionServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TaskDefinitionServiceImpl.java @@ -24,6 +24,7 @@ import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationCon import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.TASK_VERSION_VIEW; import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.WORKFLOW_DEFINITION; import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.WORKFLOW_SWITCH_TO_THIS_VERSION; +import static org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager.checkTaskParameters; import org.apache.dolphinscheduler.api.dto.task.TaskCreateRequest; import org.apache.dolphinscheduler.api.dto.task.TaskFilterRequest; @@ -67,8 +68,6 @@ import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionLogMapper; import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper; import org.apache.dolphinscheduler.dao.repository.ProcessTaskRelationLogDao; import org.apache.dolphinscheduler.dao.repository.TaskDefinitionDao; -import org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.service.process.ProcessService; import org.apache.commons.collections4.CollectionUtils; @@ -167,11 +166,7 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe return result; } for (TaskDefinitionLog taskDefinitionLog : taskDefinitionLogs) { - if (!TaskPluginManager.checkTaskParameters(ParametersNode.builder() - .taskType(taskDefinitionLog.getTaskType()) - .taskParams(taskDefinitionLog.getTaskParams()) - .dependence(taskDefinitionLog.getDependence()) - .build())) { + if (!checkTaskParameters(taskDefinitionLog.getTaskType(), taskDefinitionLog.getTaskParams())) { log.warn("Task definition {} parameters are invalid.", taskDefinitionLog.getName()); putMsg(result, Status.PROCESS_NODE_S_PARAMETER_INVALID, taskDefinitionLog.getName()); return result; @@ -208,11 +203,7 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe Project project = projectMapper.queryByCode(taskDefinition.getProjectCode()); projectService.checkProjectAndAuthThrowException(user, project, permissions); - if (!TaskPluginManager.checkTaskParameters(ParametersNode.builder() - .taskType(taskDefinition.getTaskType()) - .taskParams(taskDefinition.getTaskParams()) - .dependence(taskDefinition.getDependence()) - .build())) { + if (!checkTaskParameters(taskDefinition.getTaskType(), taskDefinition.getTaskParams())) { throw new ServiceException(Status.PROCESS_NODE_S_PARAMETER_INVALID, taskDefinition.getName()); } } @@ -321,12 +312,7 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe putMsg(result, Status.DATA_IS_NOT_VALID, taskDefinitionJsonObj); return result; } - if (!TaskPluginManager.checkTaskParameters(ParametersNode.builder() - .taskType(taskDefinition.getTaskType()) - .taskParams(taskDefinition.getTaskParams()) - .dependence(taskDefinition.getDependence()) - .build())) { - log.error("Task definition {} parameters are invalid", taskDefinition.getName()); + if (!checkTaskParameters(taskDefinition.getTaskType(), taskDefinition.getTaskParams())) { putMsg(result, Status.PROCESS_NODE_S_PARAMETER_INVALID, taskDefinition.getName()); return result; } @@ -732,13 +718,7 @@ public class TaskDefinitionServiceImpl extends BaseServiceImpl implements TaskDe putMsg(result, Status.DATA_IS_NOT_VALID, taskDefinitionJsonObj); return null; } - if (!TaskPluginManager.checkTaskParameters(ParametersNode.builder() - .taskType(taskDefinitionToUpdate.getTaskType()) - .taskParams(taskDefinitionToUpdate.getTaskParams()) - .dependence(taskDefinitionToUpdate.getDependence()) - .build())) { - log.warn("Task definition parameters are invalid, taskDefinitionName:{}.", - taskDefinitionToUpdate.getName()); + if (!checkTaskParameters(taskDefinitionToUpdate.getTaskType(), taskDefinitionToUpdate.getTaskParams())) { putMsg(result, Status.PROCESS_NODE_S_PARAMETER_INVALID, taskDefinitionToUpdate.getName()); return null; } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TenantServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TenantServiceImpl.java index e77baed1d3..7bec4d6780 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TenantServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/TenantServiceImpl.java @@ -39,7 +39,7 @@ import org.apache.dolphinscheduler.dao.mapper.ProcessInstanceMapper; import org.apache.dolphinscheduler.dao.mapper.ScheduleMapper; import org.apache.dolphinscheduler.dao.mapper.TenantMapper; import org.apache.dolphinscheduler.dao.mapper.UserMapper; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -84,7 +84,7 @@ public class TenantServiceImpl extends BaseServiceImpl implements TenantService private QueueService queueService; @Autowired(required = false) - private StorageOperate storageOperate; + private StorageOperator storageOperator; /** * Check the tenant new object valid or not @@ -136,14 +136,13 @@ public class TenantServiceImpl extends BaseServiceImpl implements TenantService * @param queueId queue id * @param desc description * @return create result code - * @throws Exception exception */ @Override @Transactional(rollbackFor = Exception.class) public Tenant createTenant(User loginUser, String tenantCode, int queueId, - String desc) throws Exception { + String desc) { if (!canOperatorPermissions(loginUser, null, AuthorizationType.TENANT, TENANT_CREATE)) { throw new ServiceException(Status.USER_NO_OPERATION_PERM); } @@ -154,7 +153,6 @@ public class TenantServiceImpl extends BaseServiceImpl implements TenantService createTenantValid(tenant); tenantMapper.insert(tenant); - storageOperate.createTenantDirIfNotExists(tenantCode); return tenant; } @@ -209,11 +207,6 @@ public class TenantServiceImpl extends BaseServiceImpl implements TenantService updateTenantValid(existsTenant, updateTenant); updateTenant.setCreateTime(existsTenant.getCreateTime()); - // updateProcessInstance tenant - // if the tenant code is modified, the original resource needs to be copied to the new tenant. - if (!Objects.equals(existsTenant.getTenantCode(), updateTenant.getTenantCode())) { - storageOperate.createTenantDirIfNotExists(tenantCode); - } int update = tenantMapper.updateById(updateTenant); if (update <= 0) { throw new ServiceException(Status.UPDATE_TENANT_ERROR); @@ -262,7 +255,6 @@ public class TenantServiceImpl extends BaseServiceImpl implements TenantService } processInstanceMapper.updateProcessInstanceByTenantCode(tenant.getTenantCode(), Constants.DEFAULT); - storageOperate.deleteTenant(tenant.getTenantCode()); } private List getProcessInstancesByTenant(Tenant tenant) { diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UdfFuncServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UdfFuncServiceImpl.java index 1bf7d23a6b..7cf16b3567 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UdfFuncServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UdfFuncServiceImpl.java @@ -28,11 +28,10 @@ import org.apache.dolphinscheduler.dao.entity.UdfFunc; import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.mapper.UDFUserMapper; import org.apache.dolphinscheduler.dao.mapper.UdfFuncMapper; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.lang3.StringUtils; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -62,7 +61,7 @@ public class UdfFuncServiceImpl extends BaseServiceImpl implements UdfFuncServic private UDFUserMapper udfUserMapper; @Autowired(required = false) - private StorageOperate storageOperate; + private StorageOperator storageOperator; /** * create udf function @@ -107,12 +106,7 @@ public class UdfFuncServiceImpl extends BaseServiceImpl implements UdfFuncServic return result; } - Boolean existResource = false; - try { - existResource = storageOperate.exists(fullName); - } catch (IOException e) { - log.error("Check resource error: {}", fullName, e); - } + boolean existResource = storageOperator.exists(fullName); if (!existResource) { log.error("resource full name {} is not exist", fullName); @@ -241,7 +235,7 @@ public class UdfFuncServiceImpl extends BaseServiceImpl implements UdfFuncServic Boolean doesResExist = false; try { - doesResExist = storageOperate.exists(fullName); + doesResExist = storageOperator.exists(fullName); } catch (Exception e) { log.error("udf resource :{} checking error", fullName, e); result.setCode(Status.RESOURCE_NOT_EXIST.getCode()); diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java index 7b9746921c..0e91dc582d 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/UsersServiceImpl.java @@ -19,7 +19,6 @@ package org.apache.dolphinscheduler.api.service.impl; import static org.apache.dolphinscheduler.api.constants.ApiFuncIdentificationConstant.USER_MANAGER; -import org.apache.dolphinscheduler.api.dto.resources.ResourceComponent; import org.apache.dolphinscheduler.api.enums.Status; import org.apache.dolphinscheduler.api.exceptions.ServiceException; import org.apache.dolphinscheduler.api.service.MetricsCleanUpService; @@ -50,7 +49,7 @@ import org.apache.dolphinscheduler.dao.mapper.ProjectUserMapper; import org.apache.dolphinscheduler.dao.mapper.TenantMapper; import org.apache.dolphinscheduler.dao.mapper.UDFUserMapper; import org.apache.dolphinscheduler.dao.mapper.UserMapper; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -110,7 +109,7 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService { private ProjectMapper projectMapper; @Autowired(required = false) - private StorageOperate storageOperate; + private StorageOperator storageOperator; @Autowired private K8sNamespaceUserMapper k8sNamespaceUserMapper; @@ -171,9 +170,6 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService { User user = createUser(userName, userPassword, email, tenantId, phone, queue, state); - Tenant tenant = tenantMapper.queryById(tenantId); - storageOperate.createTenantDirIfNotExists(tenant.getTenantCode()); - log.info("User is created and id is {}.", user.getId()); result.put(Constants.DATA_LIST, user); putMsg(result, Status.SUCCESS); @@ -1128,54 +1124,6 @@ public class UsersServiceImpl extends BaseServiceImpl implements UsersService { return msg; } - /** - * copy resource files - * xxx unchecked - * - * @param resourceComponent resource component - * @param srcBasePath src base path - * @param dstBasePath dst base path - * @throws IOException io exception - */ - private void copyResourceFiles(String oldTenantCode, String newTenantCode, ResourceComponent resourceComponent, - String srcBasePath, String dstBasePath) { - List components = resourceComponent.getChildren(); - - try { - if (CollectionUtils.isNotEmpty(components)) { - for (ResourceComponent component : components) { - // verify whether exist - if (!storageOperate.exists( - String.format(Constants.FORMAT_S_S, srcBasePath, component.getFullName()))) { - log.error("Resource file: {} does not exist, copy error.", component.getFullName()); - throw new ServiceException(Status.RESOURCE_NOT_EXIST); - } - - if (!component.isDirctory()) { - // copy it to dst - storageOperate.copy(String.format(Constants.FORMAT_S_S, srcBasePath, component.getFullName()), - String.format(Constants.FORMAT_S_S, dstBasePath, component.getFullName()), false, true); - continue; - } - - if (CollectionUtils.isEmpty(component.getChildren())) { - // if not exist,need create it - if (!storageOperate - .exists(String.format(Constants.FORMAT_S_S, dstBasePath, component.getFullName()))) { - storageOperate.mkdir(newTenantCode, - String.format(Constants.FORMAT_S_S, dstBasePath, component.getFullName())); - } - } else { - copyResourceFiles(oldTenantCode, newTenantCode, component, srcBasePath, dstBasePath); - } - } - - } - } catch (IOException e) { - log.error("copy the resources failed,the error message is {}", e.getMessage()); - } - } - /** * registry user, default state is 0, default tenant_id is 1, no phone, no queue * diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkFlowLineageServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkFlowLineageServiceImpl.java index 014d22af57..9763f21b0f 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkFlowLineageServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkFlowLineageServiceImpl.java @@ -17,8 +17,6 @@ package org.apache.dolphinscheduler.api.service.impl; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_DEPENDENT; - import org.apache.dolphinscheduler.api.enums.Status; import org.apache.dolphinscheduler.api.exceptions.ServiceException; import org.apache.dolphinscheduler.api.service.WorkFlowLineageService; @@ -39,6 +37,7 @@ import org.apache.dolphinscheduler.dao.mapper.WorkFlowLineageMapper; import org.apache.dolphinscheduler.plugin.task.api.model.DependentItem; import org.apache.dolphinscheduler.plugin.task.api.model.DependentTaskModel; import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameters; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import org.apache.commons.lang3.StringUtils; @@ -147,7 +146,7 @@ public class WorkFlowLineageServiceImpl extends BaseServiceImpl implements WorkF List processDefinitionCodes) { for (DependentProcessDefinition dependentProcessDefinition : dependentDefinitionList) { for (DependentTaskModel dependentTaskModel : dependentProcessDefinition.getDependentParameters() - .getDependTaskList()) { + .getDependence().getDependTaskList()) { for (DependentItem dependentItem : dependentTaskModel.getDependItemList()) { if (!processDefinitionCodes.contains(dependentItem.getDefinitionCode())) { processDefinitionCodes.add(dependentItem.getDefinitionCode()); @@ -220,12 +219,12 @@ public class WorkFlowLineageServiceImpl extends BaseServiceImpl implements WorkF List taskDefinitionLogs = taskDefinitionLogMapper.queryByTaskDefinitions(taskDefinitionList); for (TaskDefinitionLog taskDefinitionLog : taskDefinitionLogs) { if (taskDefinitionLog.getProjectCode() == projectCode) { - if (taskDefinitionLog.getTaskType().equals(TASK_TYPE_DEPENDENT)) { + if (TaskTypeUtils.isDependentTask(taskDefinitionLog.getTaskType())) { DependentParameters dependentParameters = JSONUtils.parseObject(taskDefinitionLog.getDependence(), DependentParameters.class); if (dependentParameters != null) { List dependTaskList = - dependentParameters.getDependTaskList(); + dependentParameters.getDependence().getDependTaskList(); if (!CollectionUtils.isEmpty(dependTaskList)) { for (DependentTaskModel taskModel : dependTaskList) { List dependItemList = taskModel.getDependItemList(); @@ -247,9 +246,9 @@ public class WorkFlowLineageServiceImpl extends BaseServiceImpl implements WorkF /** * Query and return tasks dependence with string format, is a wrapper of queryTaskDepOnTask and task query method. * - * @param projectCode Project code want to query tasks dependence + * @param projectCode Project code want to query tasks dependence * @param processDefinitionCode Process definition code want to query tasks dependence - * @param taskCode Task code want to query tasks dependence + * @param taskCode Task code want to query tasks dependence * @return Optional of formatter message */ @Override @@ -271,7 +270,7 @@ public class WorkFlowLineageServiceImpl extends BaseServiceImpl implements WorkF /** * Query tasks depend on process definition, include upstream or downstream * - * @param projectCode Project code want to query tasks dependence + * @param projectCode Project code want to query tasks dependence * @param processDefinitionCode Process definition code want to query tasks dependence * @return Set of TaskMainInfo */ @@ -291,7 +290,7 @@ public class WorkFlowLineageServiceImpl extends BaseServiceImpl implements WorkF * Query downstream tasks depend on a process definition or a task * * @param processDefinitionCode Process definition code want to query tasks dependence - * @param taskCode Task code want to query tasks dependence + * @param taskCode Task code want to query tasks dependence * @return downstream dependent tasks */ @Override diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkerGroupServiceImpl.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkerGroupServiceImpl.java index 9c98880740..4652347987 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkerGroupServiceImpl.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/WorkerGroupServiceImpl.java @@ -32,7 +32,6 @@ import org.apache.dolphinscheduler.dao.entity.EnvironmentWorkerGroupRelation; import org.apache.dolphinscheduler.dao.entity.ProcessInstance; import org.apache.dolphinscheduler.dao.entity.Schedule; import org.apache.dolphinscheduler.dao.entity.TaskDefinition; -import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.WorkerGroup; import org.apache.dolphinscheduler.dao.mapper.EnvironmentWorkerGroupRelationMapper; @@ -41,6 +40,7 @@ import org.apache.dolphinscheduler.dao.mapper.ProcessInstanceMapper; import org.apache.dolphinscheduler.dao.mapper.ScheduleMapper; import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper; import org.apache.dolphinscheduler.dao.mapper.WorkerGroupMapper; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.registry.api.RegistryClient; import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType; import org.apache.dolphinscheduler.service.process.ProcessService; @@ -357,12 +357,12 @@ public class WorkerGroupServiceImpl extends BaseServiceImpl implements WorkerGro workerGroups = workerGroupMapper.queryAllWorkerGroup(); } boolean containDefaultWorkerGroups = workerGroups.stream() - .anyMatch(workerGroup -> Constants.DEFAULT_WORKER_GROUP.equals(workerGroup.getName())); + .anyMatch(workerGroup -> WorkerGroupUtils.isWorkerGroupEmpty(workerGroup.getName())); if (!containDefaultWorkerGroups) { // there doesn't exist a default WorkerGroup, we will add all worker to the default worker group. Set activeWorkerNodes = registryClient.getServerNodeSet(RegistryNodeType.WORKER); WorkerGroup defaultWorkerGroup = new WorkerGroup(); - defaultWorkerGroup.setName(Constants.DEFAULT_WORKER_GROUP); + defaultWorkerGroup.setName(WorkerGroupUtils.getDefaultWorkerGroup()); defaultWorkerGroup.setAddrList(String.join(Constants.COMMA, activeWorkerNodes)); defaultWorkerGroup.setCreateTime(new Date()); defaultWorkerGroup.setUpdateTime(new Date()); @@ -431,27 +431,6 @@ public class WorkerGroupServiceImpl extends BaseServiceImpl implements WorkerGro return result; } - @Override - public String getTaskWorkerGroup(TaskInstance taskInstance) { - if (taskInstance == null) { - return null; - } - - String workerGroup = taskInstance.getWorkerGroup(); - - if (StringUtils.isNotEmpty(workerGroup)) { - return workerGroup; - } - int processInstanceId = taskInstance.getProcessInstanceId(); - ProcessInstance processInstance = processService.findProcessInstanceById(processInstanceId); - - if (processInstance != null) { - return processInstance.getWorkerGroup(); - } - log.info("task : {} will use default worker group", taskInstance.getId()); - return Constants.DEFAULT_WORKER_GROUP; - } - @Override public Map queryWorkerGroupByProcessDefinitionCodes(List processDefinitionCodeList) { List processDefinitionScheduleList = diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/utils/PageInfo.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/utils/PageInfo.java index 2cfdd8f840..85e47c96b9 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/utils/PageInfo.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/utils/PageInfo.java @@ -20,12 +20,16 @@ package org.apache.dolphinscheduler.api.utils; import java.util.Collections; import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.Setter; import com.baomidou.mybatisplus.core.metadata.IPage; @Data +@Builder +@AllArgsConstructor public class PageInfo { /** diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/ITransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/ITransformer.java new file mode 100644 index 0000000000..8aedad3a90 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/ITransformer.java @@ -0,0 +1,24 @@ +/* + * 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.dolphinscheduler.api.validator; + +public interface ITransformer { + + R transform(T t); + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/IValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/IValidator.java new file mode 100644 index 0000000000..7570fa67d5 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/IValidator.java @@ -0,0 +1,24 @@ +/* + * 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.dolphinscheduler.api.validator; + +public interface IValidator { + + void validate(T t); + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/AbstractResourceTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/AbstractResourceTransformer.java new file mode 100644 index 0000000000..4f721188dc --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/AbstractResourceTransformer.java @@ -0,0 +1,56 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.enums.Status; +import org.apache.dolphinscheduler.api.exceptions.ServiceException; +import org.apache.dolphinscheduler.api.validator.ITransformer; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import org.apache.commons.lang3.StringUtils; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public abstract class AbstractResourceTransformer implements ITransformer { + + protected TenantDao tenantDao; + + protected StorageOperator storageOperator; + + protected String getParentDirectoryAbsolutePath(User loginUser, String parentAbsoluteDirectory, ResourceType type) { + String tenantCode = tenantDao.queryOptionalById(loginUser.getTenantId()) + .orElseThrow(() -> new ServiceException(Status.CURRENT_LOGIN_USER_TENANT_NOT_EXIST)) + .getTenantCode(); + String userResRootPath = storageOperator.getStorageBaseDirectory(tenantCode, type); + // If the parent directory is / then will transform to userResRootPath + // This only happens when the front-end go into the resource page first + // todo: we need to change the front-end logic to avoid this + if (parentAbsoluteDirectory.equals("/")) { + return userResRootPath; + } + + if (!StringUtils.startsWith(parentAbsoluteDirectory, userResRootPath)) { + throw new ServiceException(Status.ILLEGAL_RESOURCE_PATH, parentAbsoluteDirectory); + } + return parentAbsoluteDirectory; + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/AbstractResourceValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/AbstractResourceValidator.java new file mode 100644 index 0000000000..35656b4d82 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/AbstractResourceValidator.java @@ -0,0 +1,138 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.AbstractResourceDto; +import org.apache.dolphinscheduler.api.enums.Status; +import org.apache.dolphinscheduler.api.exceptions.ServiceException; +import org.apache.dolphinscheduler.api.validator.IValidator; +import org.apache.dolphinscheduler.common.enums.UserType; +import org.apache.dolphinscheduler.common.utils.FileUtils; +import org.apache.dolphinscheduler.dao.entity.Tenant; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.web.multipart.MultipartFile; + +import com.google.common.io.Files; + +public abstract class AbstractResourceValidator implements IValidator { + + private static final Set FILE_SUFFIXES_WHICH_CAN_FETCH_CONTENT = new HashSet<>(Arrays.asList( + StringUtils.defaultIfBlank(FileUtils.getResourceViewSuffixes(), "").split(","))); + + protected final StorageOperator storageOperator; + + private final TenantDao tenantDao; + + public AbstractResourceValidator(StorageOperator storageOperator, TenantDao tenantDao) { + this.storageOperator = storageOperator; + this.tenantDao = tenantDao; + } + + public void exceptionResourceAbsolutePathInvalidated(String resourceAbsolutePath) { + if (StringUtils.isBlank(resourceAbsolutePath)) { + throw new ServiceException("The resource path is null"); + } + if (!resourceAbsolutePath.startsWith(storageOperator.getStorageBaseDirectory())) { + throw new ServiceException("Invalidated resource path: " + resourceAbsolutePath); + } + if (resourceAbsolutePath.contains("..")) { + throw new ServiceException("Invalidated resource path: " + resourceAbsolutePath); + } + } + + public void exceptionFileInvalidated(MultipartFile file) { + if (file == null) { + throw new ServiceException("The file is null"); + } + } + + public void exceptionFileContentInvalidated(String fileContent) { + if (StringUtils.isEmpty(fileContent)) { + throw new ServiceException("The file content is null"); + } + } + + public void exceptionFileContentCannotFetch(String fileAbsolutePath) { + String fileExtension = Files.getFileExtension(fileAbsolutePath); + if (!FILE_SUFFIXES_WHICH_CAN_FETCH_CONTENT.contains(fileExtension)) { + throw new ServiceException("The file type: " + fileExtension + " cannot be fetched"); + } + } + + public void exceptionResourceNotExists(String resourceAbsolutePath) { + if (!storageOperator.exists(resourceAbsolutePath)) { + throw new ServiceException("Thr resource is not exists: " + resourceAbsolutePath); + } + } + + public void exceptionResourceExists(String resourceAbsolutePath) { + if (storageOperator.exists(resourceAbsolutePath)) { + throw new ServiceException("The resource is already exist: " + resourceAbsolutePath); + } + } + + public void exceptionResourceIsNotDirectory(String resourceAbsolutePath) { + if (StringUtils.isNotEmpty(Files.getFileExtension(resourceAbsolutePath))) { + throw new ServiceException("The path is not a directory: " + resourceAbsolutePath); + } + } + + public void exceptionResourceIsNotFile(String fileAbsolutePath) { + if (StringUtils.isEmpty(Files.getFileExtension(fileAbsolutePath))) { + throw new ServiceException("The path is not a file: " + fileAbsolutePath); + } + } + + public void exceptionUserNoResourcePermission(User user, AbstractResourceDto resourceDto) { + exceptionUserNoResourcePermission(user, resourceDto.getResourceAbsolutePath()); + } + + public void exceptionUserNoResourcePermission(User user, String resourceAbsolutePath) { + if (user.getUserType() == UserType.ADMIN_USER) { + return; + } + // check if the user have resource tenant permission + // Parse the resource path to get the tenant code + ResourceMetadata resourceMetaData = storageOperator.getResourceMetaData(resourceAbsolutePath); + + if (!resourceAbsolutePath.startsWith(resourceMetaData.getResourceBaseDirectory())) { + throw new ServiceException("Invalidated resource path: " + resourceAbsolutePath); + } + + // todo: inject the tenant when login + Tenant tenant = tenantDao.queryOptionalById(user.getTenantId()) + .orElseThrow(() -> new ServiceException(Status.TENANT_NOT_EXIST, user.getTenantId())); + String userTenant = tenant.getTenantCode(); + if (!userTenant.equals(resourceMetaData.getTenant())) { + throw new ServiceException( + "The user's tenant is " + userTenant + " have no permission to access the resource: " + + resourceAbsolutePath); + } + } + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryDtoValidator.java new file mode 100644 index 0000000000..248f7fbff3 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryDtoValidator.java @@ -0,0 +1,50 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.CreateDirectoryDto; +import org.apache.dolphinscheduler.api.exceptions.ServiceException; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.apache.commons.lang3.StringUtils; + +import org.springframework.stereotype.Component; + +import com.google.common.io.Files; + +@Component +public class CreateDirectoryDtoValidator extends AbstractResourceValidator { + + public CreateDirectoryDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(CreateDirectoryDto createDirectoryDto) { + String directoryAbsolutePath = createDirectoryDto.getDirectoryAbsolutePath(); + + exceptionResourceAbsolutePathInvalidated(directoryAbsolutePath); + exceptionResourceExists(directoryAbsolutePath); + exceptionUserNoResourcePermission(createDirectoryDto.getLoginUser(), directoryAbsolutePath); + exceptionResourceIsNotDirectory(directoryAbsolutePath); + if (StringUtils.isNotEmpty(Files.getFileExtension(directoryAbsolutePath))) { + throw new ServiceException("The path is not a directory: " + directoryAbsolutePath); + } + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryRequestTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryRequestTransformer.java new file mode 100644 index 0000000000..75985477f3 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryRequestTransformer.java @@ -0,0 +1,87 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.dolphinscheduler.api.dto.resources.CreateDirectoryDto; +import org.apache.dolphinscheduler.api.dto.resources.CreateDirectoryRequest; +import org.apache.dolphinscheduler.api.enums.Status; +import org.apache.dolphinscheduler.api.exceptions.ServiceException; +import org.apache.dolphinscheduler.api.validator.ITransformer; +import org.apache.dolphinscheduler.common.utils.FileUtils; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.apache.commons.lang3.StringUtils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class CreateDirectoryRequestTransformer implements ITransformer { + + @Autowired + private TenantDao tenantDao; + + @Autowired + private StorageOperator storageOperator; + + @Override + public CreateDirectoryDto transform(CreateDirectoryRequest createDirectoryRequest) { + validateCreateDirectoryRequest(createDirectoryRequest); + return doTransform(createDirectoryRequest); + } + + private CreateDirectoryDto doTransform(CreateDirectoryRequest createDirectoryRequest) { + String directoryAbsolutePath = getDirectoryAbsolutePath(createDirectoryRequest); + return CreateDirectoryDto.builder() + .loginUser(createDirectoryRequest.getLoginUser()) + .directoryAbsolutePath(directoryAbsolutePath) + .build(); + } + + private void validateCreateDirectoryRequest(CreateDirectoryRequest createDirectoryRequest) { + checkNotNull(createDirectoryRequest.getLoginUser(), "loginUser is null"); + checkNotNull(createDirectoryRequest.getType(), "resource type is null"); + checkNotNull(createDirectoryRequest.getDirectoryName(), "directory name is null"); + checkNotNull(createDirectoryRequest.getParentAbsoluteDirectory(), "parent directory is null"); + + } + + private String getDirectoryAbsolutePath(CreateDirectoryRequest createDirectoryRequest) { + String tenantCode = tenantDao.queryOptionalById(createDirectoryRequest.getLoginUser().getTenantId()) + .orElseThrow(() -> new ServiceException(Status.CURRENT_LOGIN_USER_TENANT_NOT_EXIST)) + .getTenantCode(); + String userResRootPath = storageOperator.getStorageBaseDirectory(tenantCode, createDirectoryRequest.getType()); + String parentDirectoryName = createDirectoryRequest.getParentAbsoluteDirectory(); + String directoryName = createDirectoryRequest.getDirectoryName(); + + // If the parent directory is / then will transform to userResRootPath + // This only happens when the front-end go into the resource page first + // todo: we need to change the front-end logic to avoid this + if (parentDirectoryName.equals("/")) { + return FileUtils.concatFilePath(userResRootPath, directoryName); + } + + if (!StringUtils.startsWith(parentDirectoryName, userResRootPath)) { + throw new ServiceException(Status.ILLEGAL_RESOURCE_PATH, parentDirectoryName); + } + return FileUtils.concatFilePath(parentDirectoryName, directoryName); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileDtoValidator.java new file mode 100644 index 0000000000..d2c91387a6 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileDtoValidator.java @@ -0,0 +1,47 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.CreateFileDto; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component +public class CreateFileDtoValidator extends AbstractResourceValidator { + + public CreateFileDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(CreateFileDto createFileDto) { + String fileAbsolutePath = createFileDto.getFileAbsolutePath(); + User loginUser = createFileDto.getLoginUser(); + MultipartFile file = createFileDto.getFile(); + + exceptionResourceAbsolutePathInvalidated(fileAbsolutePath); + exceptionResourceExists(fileAbsolutePath); + exceptionFileInvalidated(file); + exceptionUserNoResourcePermission(loginUser, fileAbsolutePath); + exceptionResourceIsNotFile(fileAbsolutePath); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileFromContentDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileFromContentDtoValidator.java new file mode 100644 index 0000000000..ae61f47041 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileFromContentDtoValidator.java @@ -0,0 +1,46 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.CreateFileFromContentDto; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class CreateFileFromContentDtoValidator extends AbstractResourceValidator { + + public CreateFileFromContentDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(CreateFileFromContentDto createFileFromContentDto) { + String fileAbsolutePath = createFileFromContentDto.getFileAbsolutePath(); + User loginUser = createFileFromContentDto.getLoginUser(); + String fileContent = createFileFromContentDto.getFileContent(); + + exceptionResourceAbsolutePathInvalidated(fileAbsolutePath); + exceptionResourceIsNotFile(fileAbsolutePath); + exceptionResourceExists(fileAbsolutePath); + exceptionUserNoResourcePermission(loginUser, fileAbsolutePath); + exceptionFileContentInvalidated(fileContent); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/DeleteResourceDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/DeleteResourceDtoValidator.java new file mode 100644 index 0000000000..e337d9e07c --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/DeleteResourceDtoValidator.java @@ -0,0 +1,43 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.DeleteResourceDto; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class DeleteResourceDtoValidator extends AbstractResourceValidator { + + public DeleteResourceDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(DeleteResourceDto deleteResourceDto) { + String resourceAbsolutePath = deleteResourceDto.getResourceAbsolutePath(); + User loginUser = deleteResourceDto.getLoginUser(); + + exceptionResourceAbsolutePathInvalidated(resourceAbsolutePath); + exceptionResourceNotExists(resourceAbsolutePath); + exceptionUserNoResourcePermission(loginUser, resourceAbsolutePath); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/DownloadFileDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/DownloadFileDtoValidator.java new file mode 100644 index 0000000000..ac15d279ac --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/DownloadFileDtoValidator.java @@ -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. + */ + +package org.apache.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.DownloadFileDto; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class DownloadFileDtoValidator extends AbstractResourceValidator { + + public DownloadFileDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(DownloadFileDto downloadFileDto) { + String fileAbsolutePath = downloadFileDto.getFileAbsolutePath(); + User loginUser = downloadFileDto.getLoginUser(); + + exceptionResourceNotExists(fileAbsolutePath); + exceptionResourceAbsolutePathInvalidated(fileAbsolutePath); + exceptionResourceIsNotFile(fileAbsolutePath); + exceptionUserNoResourcePermission(loginUser, fileAbsolutePath); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FetchFileContentDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FetchFileContentDtoValidator.java new file mode 100644 index 0000000000..b69ec68a7c --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FetchFileContentDtoValidator.java @@ -0,0 +1,48 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.FetchFileContentDto; +import org.apache.dolphinscheduler.api.exceptions.ServiceException; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class FetchFileContentDtoValidator extends AbstractResourceValidator { + + public FetchFileContentDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(FetchFileContentDto fetchFileContentDto) { + if (fetchFileContentDto.getSkipLineNum() < 0) { + throw new ServiceException("skipLineNum must be greater than or equal to 0"); + } + String resourceFileAbsolutePath = fetchFileContentDto.getResourceFileAbsolutePath(); + User loginUser = fetchFileContentDto.getLoginUser(); + + exceptionResourceAbsolutePathInvalidated(resourceFileAbsolutePath); + exceptionResourceIsNotFile(resourceFileAbsolutePath); + exceptionUserNoResourcePermission(loginUser, resourceFileAbsolutePath); + exceptionFileContentCannotFetch(resourceFileAbsolutePath); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FileFromContentRequestTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FileFromContentRequestTransformer.java new file mode 100644 index 0000000000..7f29646e36 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FileFromContentRequestTransformer.java @@ -0,0 +1,71 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.dolphinscheduler.api.dto.resources.CreateFileFromContentDto; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileFromContentRequest; +import org.apache.dolphinscheduler.common.utils.FileUtils; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class FileFromContentRequestTransformer + extends + AbstractResourceTransformer { + + public FileFromContentRequestTransformer(TenantDao tenantDao, StorageOperator storageOperator) { + super(tenantDao, storageOperator); + } + + @Override + public CreateFileFromContentDto transform(CreateFileFromContentRequest createFileFromContentRequest) { + validateCreateFileRequest(createFileFromContentRequest); + return doTransform(createFileFromContentRequest); + } + + private void validateCreateFileRequest(CreateFileFromContentRequest createFileFromContentRequest) { + checkNotNull(createFileFromContentRequest.getLoginUser(), "loginUser is null"); + checkNotNull(createFileFromContentRequest.getType(), "resource type is null"); + checkNotNull(createFileFromContentRequest.getFileName(), "file name is null"); + checkNotNull(createFileFromContentRequest.getParentAbsoluteDirectory(), "parent directory is null"); + checkNotNull(createFileFromContentRequest.getFileContent(), "file content is null"); + } + + private CreateFileFromContentDto doTransform(CreateFileFromContentRequest createFileFromContentRequest) { + String fileAbsolutePath = getFileAbsolutePath(createFileFromContentRequest); + return CreateFileFromContentDto.builder() + .loginUser(createFileFromContentRequest.getLoginUser()) + .fileAbsolutePath(fileAbsolutePath) + .fileContent(createFileFromContentRequest.getFileContent()) + .build(); + + } + + private String getFileAbsolutePath(CreateFileFromContentRequest createFileFromContentRequest) { + String parentDirectoryAbsolutePath = getParentDirectoryAbsolutePath( + createFileFromContentRequest.getLoginUser(), + createFileFromContentRequest.getParentAbsoluteDirectory(), + createFileFromContentRequest.getType()); + return FileUtils.concatFilePath(parentDirectoryAbsolutePath, createFileFromContentRequest.getFileName()); + } + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FileRequestTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FileRequestTransformer.java new file mode 100644 index 0000000000..c2f007dbf7 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/FileRequestTransformer.java @@ -0,0 +1,68 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.dolphinscheduler.api.dto.resources.CreateFileDto; +import org.apache.dolphinscheduler.api.dto.resources.CreateFileRequest; +import org.apache.dolphinscheduler.common.utils.FileUtils; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class FileRequestTransformer extends AbstractResourceTransformer { + + public FileRequestTransformer(TenantDao tenantDao, StorageOperator storageOperator) { + super(tenantDao, storageOperator); + } + + @Override + public CreateFileDto transform(CreateFileRequest createFileRequest) { + validateCreateFileRequest(createFileRequest); + return doTransform(createFileRequest); + } + + private void validateCreateFileRequest(CreateFileRequest createFileRequest) { + checkNotNull(createFileRequest.getLoginUser(), "loginUser is null"); + checkNotNull(createFileRequest.getType(), "resource type is null"); + checkNotNull(createFileRequest.getFileName(), "file name is null"); + checkNotNull(createFileRequest.getParentAbsoluteDirectory(), "parent directory is null"); + checkNotNull(createFileRequest.getFile(), "file is null"); + } + + private CreateFileDto doTransform(CreateFileRequest createFileRequest) { + String fileAbsolutePath = getFileAbsolutePath(createFileRequest); + return CreateFileDto.builder() + .loginUser(createFileRequest.getLoginUser()) + .file(createFileRequest.getFile()) + .fileAbsolutePath(fileAbsolutePath) + .build(); + + } + + private String getFileAbsolutePath(CreateFileRequest createFileRequest) { + String parentDirectoryAbsolutePath = getParentDirectoryAbsolutePath( + createFileRequest.getLoginUser(), + createFileRequest.getParentAbsoluteDirectory(), + createFileRequest.getType()); + return FileUtils.concatFilePath(parentDirectoryAbsolutePath, createFileRequest.getFileName()); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/PagingResourceItemRequestTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/PagingResourceItemRequestTransformer.java new file mode 100644 index 0000000000..9be51e413e --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/PagingResourceItemRequestTransformer.java @@ -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. + */ + +package org.apache.dolphinscheduler.api.validator.resource; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.dolphinscheduler.api.dto.resources.PagingResourceItemRequest; +import org.apache.dolphinscheduler.api.dto.resources.QueryResourceDto; +import org.apache.dolphinscheduler.api.validator.ITransformer; +import org.apache.dolphinscheduler.common.enums.UserType; +import org.apache.dolphinscheduler.dao.entity.Tenant; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.stream.Collectors; + +import lombok.AllArgsConstructor; + +import org.springframework.stereotype.Component; + +import com.google.common.collect.Lists; + +@Component +@AllArgsConstructor +public class PagingResourceItemRequestTransformer implements ITransformer { + + private final StorageOperator storageOperator; + + private final TenantDao tenantDao; + + @Override + public QueryResourceDto transform(PagingResourceItemRequest pagingResourceItemRequest) { + validatePagingResourceItemRequest(pagingResourceItemRequest); + + if (StringUtils.isNotEmpty(pagingResourceItemRequest.getResourceAbsolutePath())) { + // query from the given path + return QueryResourceDto.builder() + .resourceAbsolutePaths(Lists.newArrayList(pagingResourceItemRequest.getResourceAbsolutePath())) + .build(); + } + + ResourceType resourceType = pagingResourceItemRequest.getResourceType(); + User loginUser = pagingResourceItemRequest.getLoginUser(); + if (loginUser.getUserType() == UserType.ADMIN_USER) { + // If the current user is admin + // then will query all tenant resources + List resourceAbsolutePaths = tenantDao.queryAll() + .stream() + .map(tenant -> storageOperator.getStorageBaseDirectory(tenant.getTenantCode(), resourceType)) + .collect(Collectors.toList()); + return QueryResourceDto.builder() + .resourceAbsolutePaths(resourceAbsolutePaths) + .build(); + } else { + // todo: inject the tenantCode when login + Tenant tenant = tenantDao.queryById(loginUser.getTenantId()); + String storageBaseDirectory = storageOperator.getStorageBaseDirectory(tenant.getTenantCode(), resourceType); + return QueryResourceDto.builder() + .resourceAbsolutePaths(Lists.newArrayList(storageBaseDirectory)) + .build(); + } + + } + + private void validatePagingResourceItemRequest(PagingResourceItemRequest pagingResourceItemRequest) { + checkNotNull(pagingResourceItemRequest.getLoginUser(), "loginUser is null"); + checkNotNull(pagingResourceItemRequest.getResourceType(), "resourceType is null"); + } + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryDtoValidator.java new file mode 100644 index 0000000000..b76ea5b6fc --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryDtoValidator.java @@ -0,0 +1,50 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.RenameDirectoryDto; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class RenameDirectoryDtoValidator extends AbstractResourceValidator { + + public RenameDirectoryDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(RenameDirectoryDto renameDirectoryDto) { + String originDirectoryAbsolutePath = renameDirectoryDto.getOriginDirectoryAbsolutePath(); + User loginUser = renameDirectoryDto.getLoginUser(); + String targetDirectoryAbsolutePath = renameDirectoryDto.getTargetDirectoryAbsolutePath(); + + exceptionResourceAbsolutePathInvalidated(originDirectoryAbsolutePath); + exceptionResourceIsNotDirectory(originDirectoryAbsolutePath); + exceptionResourceNotExists(originDirectoryAbsolutePath); + exceptionUserNoResourcePermission(loginUser, originDirectoryAbsolutePath); + + exceptionResourceAbsolutePathInvalidated(targetDirectoryAbsolutePath); + exceptionResourceIsNotDirectory(targetDirectoryAbsolutePath); + exceptionResourceExists(targetDirectoryAbsolutePath); + exceptionUserNoResourcePermission(loginUser, targetDirectoryAbsolutePath); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryRequestTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryRequestTransformer.java new file mode 100644 index 0000000000..4d6745df3d --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryRequestTransformer.java @@ -0,0 +1,56 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.RenameDirectoryDto; +import org.apache.dolphinscheduler.api.dto.resources.RenameDirectoryRequest; +import org.apache.dolphinscheduler.api.validator.ITransformer; + +import org.apache.commons.lang3.StringUtils; + +import java.io.File; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class RenameDirectoryRequestTransformer implements ITransformer { + + @Override + public RenameDirectoryDto transform(RenameDirectoryRequest renameDirectoryRequest) { + String originDirectoryAbsolutePath = renameDirectoryRequest.getDirectoryAbsolutePath(); + String targetDirectoryName = renameDirectoryRequest.getNewDirectoryName(); + + String targetDirectoryAbsolutePath = + getTargetDirectoryAbsolutePath(originDirectoryAbsolutePath, targetDirectoryName); + + return RenameDirectoryDto.builder() + .loginUser(renameDirectoryRequest.getLoginUser()) + .originDirectoryAbsolutePath(originDirectoryAbsolutePath) + .targetDirectoryAbsolutePath(targetDirectoryAbsolutePath) + .build(); + } + + private String getTargetDirectoryAbsolutePath(String originDirectoryAbsolutePath, String targetDirectoryName) { + String originDirectoryParentAbsolutePath = StringUtils.substringBeforeLast( + originDirectoryAbsolutePath, File.separator); + return originDirectoryParentAbsolutePath + File.separator + targetDirectoryName; + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileDtoValidator.java new file mode 100644 index 0000000000..8c9e333dbb --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileDtoValidator.java @@ -0,0 +1,50 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.RenameFileDto; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class RenameFileDtoValidator extends AbstractResourceValidator { + + public RenameFileDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(RenameFileDto renameFileDto) { + String originFileAbsolutePath = renameFileDto.getOriginFileAbsolutePath(); + User loginUser = renameFileDto.getLoginUser(); + String targetFileAbsolutePath = renameFileDto.getTargetFileAbsolutePath(); + + exceptionResourceAbsolutePathInvalidated(originFileAbsolutePath); + exceptionResourceNotExists(originFileAbsolutePath); + exceptionResourceIsNotFile(originFileAbsolutePath); + exceptionUserNoResourcePermission(loginUser, originFileAbsolutePath); + + exceptionResourceAbsolutePathInvalidated(targetFileAbsolutePath); + exceptionResourceExists(targetFileAbsolutePath); + exceptionResourceIsNotFile(targetFileAbsolutePath); + exceptionUserNoResourcePermission(loginUser, targetFileAbsolutePath); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileRequestTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileRequestTransformer.java new file mode 100644 index 0000000000..43cb55c461 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileRequestTransformer.java @@ -0,0 +1,47 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.RenameFileDto; +import org.apache.dolphinscheduler.api.dto.resources.RenameFileRequest; +import org.apache.dolphinscheduler.api.validator.ITransformer; +import org.apache.dolphinscheduler.common.utils.FileUtils; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RenameFileRequestTransformer implements ITransformer { + + @Autowired + private StorageOperator storageOperator; + + @Override + public RenameFileDto transform(RenameFileRequest renameFileRequest) { + ResourceMetadata resourceMetaData = + storageOperator.getResourceMetaData(renameFileRequest.getFileAbsolutePath()); + return RenameFileDto.builder() + .loginUser(renameFileRequest.getLoginUser()) + .originFileAbsolutePath(renameFileRequest.getFileAbsolutePath()) + .targetFileAbsolutePath(FileUtils.concatFilePath(resourceMetaData.getResourceParentAbsolutePath(), + renameFileRequest.getNewFileName())) + .build(); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileDtoValidator.java new file mode 100644 index 0000000000..468c651087 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileDtoValidator.java @@ -0,0 +1,57 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileDto; +import org.apache.dolphinscheduler.api.exceptions.ServiceException; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import java.util.Objects; + +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import com.google.common.io.Files; + +@Component +public class UpdateFileDtoValidator extends AbstractResourceValidator { + + public UpdateFileDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(UpdateFileDto updateFileDto) { + String fileAbsolutePath = updateFileDto.getFileAbsolutePath(); + User loginUser = updateFileDto.getLoginUser(); + MultipartFile file = updateFileDto.getFile(); + + if (!Objects.equals(Files.getFileExtension(file.getName()), + Files.getFileExtension(updateFileDto.getFileAbsolutePath()))) { + throw new ServiceException("file extension cannot not change"); + } + + exceptionResourceAbsolutePathInvalidated(fileAbsolutePath); + exceptionResourceNotExists(fileAbsolutePath); + exceptionResourceIsNotFile(fileAbsolutePath); + exceptionUserNoResourcePermission(loginUser, fileAbsolutePath); + exceptionFileInvalidated(file); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileFromContentDtoValidator.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileFromContentDtoValidator.java new file mode 100644 index 0000000000..5245759639 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileFromContentDtoValidator.java @@ -0,0 +1,47 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileFromContentDto; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import org.springframework.stereotype.Component; + +@Component +public class UpdateFileFromContentDtoValidator extends AbstractResourceValidator { + + public UpdateFileFromContentDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) { + super(storageOperator, tenantDao); + } + + @Override + public void validate(UpdateFileFromContentDto updateFileFromContentDto) { + String fileAbsolutePath = updateFileFromContentDto.getFileAbsolutePath(); + User loginUser = updateFileFromContentDto.getLoginUser(); + String fileContent = updateFileFromContentDto.getFileContent(); + + exceptionResourceAbsolutePathInvalidated(fileAbsolutePath); + exceptionResourceNotExists(fileAbsolutePath); + exceptionResourceIsNotFile(fileAbsolutePath); + exceptionUserNoResourcePermission(loginUser, fileAbsolutePath); + exceptionFileContentCannotFetch(fileAbsolutePath); + exceptionFileContentInvalidated(fileContent); + } +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileFromContentRequestTransformer.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileFromContentRequestTransformer.java new file mode 100644 index 0000000000..961d0e6049 --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileFromContentRequestTransformer.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileFromContentDto; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileFromContentRequest; +import org.apache.dolphinscheduler.api.validator.ITransformer; + +import org.springframework.stereotype.Component; + +@Component +public class UpdateFileFromContentRequestTransformer + implements + ITransformer { + + @Override + public UpdateFileFromContentDto transform(UpdateFileFromContentRequest updateFileContentRequest) { + return UpdateFileFromContentDto.builder() + .loginUser(updateFileContentRequest.getLoginUser()) + .fileAbsolutePath(updateFileContentRequest.getFileAbsolutePath()) + .fileContent(updateFileContentRequest.getFileContent()) + .build(); + } +} diff --git a/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/common/UiChannelFactory.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileRequestTransformer.java similarity index 51% rename from dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/common/UiChannelFactory.java rename to dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileRequestTransformer.java index 8b89215049..5c7646fbf9 100644 --- a/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/common/UiChannelFactory.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileRequestTransformer.java @@ -15,29 +15,24 @@ * limitations under the License. */ -package org.apache.dolphinscheduler.spi.common; +package org.apache.dolphinscheduler.api.validator.resource; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileDto; +import org.apache.dolphinscheduler.api.dto.resources.UpdateFileRequest; +import org.apache.dolphinscheduler.api.validator.ITransformer; -import java.util.List; +import org.springframework.stereotype.Component; -public interface UiChannelFactory { +@Component +public class UpdateFileRequestTransformer implements ITransformer { - /** - * plugin name - * Must be UNIQUE . - * This alert plugin name eg: email , message ... - * Name can often be displayed on the page ui eg : email , message , MR , spark , hive ... - * - * @return this alert plugin name - */ - String getName(); - - /** - * Returns the configurable parameters that this plugin needs to display on the web ui - * - * @return this alert plugin params - */ - List getParams(); + @Override + public UpdateFileDto transform(UpdateFileRequest updateFileRequest) { + return UpdateFileDto.builder() + .loginUser(updateFileRequest.getLoginUser()) + .fileAbsolutePath(updateFileRequest.getFileAbsolutePath()) + .file(updateFileRequest.getFile()) + .build(); + } } diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/ResourceItemVO.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/ResourceItemVO.java new file mode 100644 index 0000000000..9470ded4ba --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/ResourceItemVO.java @@ -0,0 +1,74 @@ +/* + * 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.dolphinscheduler.api.vo; + +import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.util.Date; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ResourceItemVO { + + // todo: remove this field, directly use fileName + private String alias; + + // todo: use tenantName instead of userName + private String userName; + + private String fileName; + + private String fullName; + + private boolean isDirectory; + + private ResourceType type; + + private long size; + + private Date createTime; + + private Date updateTime; + + public ResourceItemVO(StorageEntity storageEntity) { + this.isDirectory = storageEntity.isDirectory(); + this.alias = storageEntity.getFileName(); + this.fileName = storageEntity.getFileName(); + this.fullName = storageEntity.getFullName(); + this.type = storageEntity.getType(); + this.size = storageEntity.getSize(); + this.createTime = storageEntity.getCreateTime(); + this.updateTime = storageEntity.getUpdateTime(); + + if (isDirectory) { + alias = StringUtils.removeEndIgnoreCase(alias, File.separator); + fileName = StringUtils.removeEndIgnoreCase(fileName, File.separator); + fullName = StringUtils.removeEndIgnoreCase(fullName, File.separator); + } + } + +} diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/resources/FetchFileContentResponse.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/resources/FetchFileContentResponse.java new file mode 100644 index 0000000000..1f228f42fb --- /dev/null +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/resources/FetchFileContentResponse.java @@ -0,0 +1,33 @@ +/* + * 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.dolphinscheduler.api.vo.resources; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FetchFileContentResponse { + + private String content; + +} diff --git a/dolphinscheduler-api/src/main/resources/application.yaml b/dolphinscheduler-api/src/main/resources/application.yaml index e38d0c5a8e..9b0e94d644 100644 --- a/dolphinscheduler-api/src/main/resources/application.yaml +++ b/dolphinscheduler-api/src/main/resources/application.yaml @@ -75,6 +75,8 @@ spring: pathmatch: matching-strategy: ANT_PATH_MATCHER static-path-pattern: /static/** + cloud.discovery.client.composite-indicator.enabled: false + springdoc: swagger-ui: path: /swagger-ui.html @@ -118,8 +120,8 @@ registry: namespace: dolphinscheduler connect-string: localhost:2181 retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms + base-sleep-time: 1s + max-sleep: 3s max-retries: 5 session-timeout: 60s connection-timeout: 15s diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/AssertionsHelper.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/AssertionsHelper.java index eae064bb24..21977bd50d 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/AssertionsHelper.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/AssertionsHelper.java @@ -27,6 +27,11 @@ import org.junit.jupiter.api.function.Executable; public class AssertionsHelper extends Assertions { + public static void assertThrowServiceException(String message, Executable executable) { + ServiceException exception = Assertions.assertThrows(ServiceException.class, executable); + Assertions.assertEquals(message, exception.getMessage()); + } + public static void assertThrowsServiceException(Status status, Executable executable) { ServiceException exception = Assertions.assertThrows(ServiceException.class, executable); Assertions.assertEquals(status.getCode(), exception.getCode()); diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ResourcesControllerTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ResourcesControllerTest.java index bfed64f9f6..01a2d47998 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ResourcesControllerTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/ResourcesControllerTest.java @@ -17,6 +17,7 @@ package org.apache.dolphinscheduler.api.controller; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -28,29 +29,24 @@ import org.apache.dolphinscheduler.api.enums.Status; import org.apache.dolphinscheduler.api.service.ResourcesService; import org.apache.dolphinscheduler.api.service.UdfFuncService; import org.apache.dolphinscheduler.api.utils.Result; -import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.api.vo.resources.FetchFileContentResponse; import org.apache.dolphinscheduler.common.enums.UdfType; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.spi.enums.ResourceType; -import java.util.HashMap; -import java.util.Map; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -/** - * resources controller test - */ +import com.fasterxml.jackson.core.type.TypeReference; + public class ResourcesControllerTest extends AbstractControllerTest { private static final Logger logger = LoggerFactory.getLogger(ResourcesControllerTest.class); @@ -61,37 +57,12 @@ public class ResourcesControllerTest extends AbstractControllerTest { @MockBean(name = "udfFuncServiceImpl") private UdfFuncService udfFuncService; - @Test - public void testQuerytResourceList() throws Exception { - Map mockResult = new HashMap<>(); - mockResult.put(Constants.STATUS, Status.SUCCESS); - Mockito.when(resourcesService.queryResourceList(Mockito.any(), Mockito.any(), Mockito.anyString())) - .thenReturn(mockResult); - - MultiValueMap paramsMap = new LinkedMultiValueMap<>(); - paramsMap.add("fullName", "dolphinscheduler/resourcePath"); - paramsMap.add("type", ResourceType.FILE.name()); - MvcResult mvcResult = mockMvc.perform(get("/resources/list") - .header(SESSION_ID, sessionId) - .params(paramsMap)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andReturn(); - - Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - - Assertions.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); - logger.info(mvcResult.getResponse().getContentAsString()); - } - @Test public void testQueryResourceListPaging() throws Exception { Result mockResult = new Result<>(); mockResult.setCode(Status.SUCCESS.getCode()); - Mockito.when(resourcesService.queryResourceListPaging( - Mockito.any(), Mockito.anyString(), Mockito.anyString(), Mockito.any(), - Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt())) - .thenReturn(mockResult); + // Mockito.when(resourcesService.pagingResourceItem() + // .thenReturn(mockResult); MultiValueMap paramsMap = new LinkedMultiValueMap<>(); paramsMap.add("type", String.valueOf(ResourceType.FILE)); @@ -111,41 +82,17 @@ public class ResourcesControllerTest extends AbstractControllerTest { Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assertions.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); - logger.info(mvcResult.getResponse().getContentAsString()); - } - - @Test - public void testVerifyResourceName() throws Exception { - Result mockResult = new Result<>(); - mockResult.setCode(Status.TENANT_NOT_EXIST.getCode()); - Mockito.when(resourcesService.verifyResourceName(Mockito.anyString(), Mockito.any(), Mockito.any())) - .thenReturn(mockResult); - - MultiValueMap paramsMap = new LinkedMultiValueMap<>(); - paramsMap.add("fullName", "list_resources_1.sh"); - paramsMap.add("type", "FILE"); - - MvcResult mvcResult = mockMvc.perform(get("/resources/verify-name") - .header(SESSION_ID, sessionId) - .params(paramsMap)) - .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andReturn(); - - Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - - Assertions.assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } @Test public void testViewResource() throws Exception { - Result mockResult = new Result<>(); - mockResult.setCode(Status.HDFS_NOT_STARTUP.getCode()); - Mockito.when(resourcesService.readResource(Mockito.any(), - Mockito.anyString(), Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt())) - .thenReturn(mockResult); + FetchFileContentResponse fetchFileContentResponse = FetchFileContentResponse.builder() + .content("echo hello") + .build(); + Mockito.when(resourcesService.fetchResourceFileContent(Mockito.any())) + .thenReturn(fetchFileContentResponse); MultiValueMap paramsMap = new LinkedMultiValueMap<>(); paramsMap.add("skipLineNum", "2"); @@ -160,19 +107,17 @@ public class ResourcesControllerTest extends AbstractControllerTest { .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andReturn(); - Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); + Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), + new TypeReference>() { + }); - Assertions.assertEquals(Status.HDFS_NOT_STARTUP.getCode(), result.getCode().intValue()); - logger.info(mvcResult.getResponse().getContentAsString()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); + assertEquals(fetchFileContentResponse, result.getData()); } @Test public void testCreateResourceFile() throws Exception { - Result mockResult = new Result<>(); - mockResult.setCode(Status.TENANT_NOT_EXIST.getCode()); - Mockito.when(resourcesService.createResourceFile(Mockito.any(), Mockito.any(), Mockito.anyString(), - Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) - .thenReturn(mockResult); + Mockito.doNothing().when(resourcesService).createFileFromContent(Mockito.any()); MultiValueMap paramsMap = new LinkedMultiValueMap<>(); paramsMap.add("type", String.valueOf(ResourceType.FILE)); @@ -190,19 +135,16 @@ public class ResourcesControllerTest extends AbstractControllerTest { .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andReturn(); - Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); + Result result = + JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() { + }); - Assertions.assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); - logger.info(mvcResult.getResponse().getContentAsString()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); } @Test public void testUpdateResourceContent() throws Exception { - Result mockResult = new Result<>(); - mockResult.setCode(Status.TENANT_NOT_EXIST.getCode()); - Mockito.when(resourcesService.updateResourceContent(Mockito.any(), Mockito.anyString(), - Mockito.anyString(), Mockito.anyString())) - .thenReturn(mockResult); + Mockito.doNothing().when(resourcesService).updateFileFromContent(Mockito.any()); MultiValueMap paramsMap = new LinkedMultiValueMap<>(); paramsMap.add("id", "1"); @@ -217,17 +159,17 @@ public class ResourcesControllerTest extends AbstractControllerTest { .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andReturn(); - Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); + Result result = + JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() { + }); - Assertions.assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); - logger.info(mvcResult.getResponse().getContentAsString()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); } @Test public void testDownloadResource() throws Exception { - Mockito.when(resourcesService.downloadResource(Mockito.any(), Mockito.anyString())) - .thenReturn(null); + Mockito.doNothing().when(resourcesService).downloadResource(Mockito.any(), Mockito.any()); MultiValueMap paramsMap = new LinkedMultiValueMap<>(); paramsMap.add("fullName", "dolphinscheduler/resourcePath"); @@ -235,7 +177,7 @@ public class ResourcesControllerTest extends AbstractControllerTest { MvcResult mvcResult = mockMvc.perform(get("/resources/download") .params(paramsMap) .header(SESSION_ID, sessionId)) - .andExpect(status().is(HttpStatus.BAD_REQUEST.value())) + .andExpect(status().isOk()) .andReturn(); Assertions.assertNotNull(mvcResult); @@ -269,7 +211,7 @@ public class ResourcesControllerTest extends AbstractControllerTest { Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assertions.assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); + assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } @@ -289,7 +231,7 @@ public class ResourcesControllerTest extends AbstractControllerTest { Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assertions.assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); + assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } @@ -323,7 +265,7 @@ public class ResourcesControllerTest extends AbstractControllerTest { Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assertions.assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); + assertEquals(Status.TENANT_NOT_EXIST.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } @@ -348,7 +290,7 @@ public class ResourcesControllerTest extends AbstractControllerTest { Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assertions.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } @@ -370,7 +312,7 @@ public class ResourcesControllerTest extends AbstractControllerTest { Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assertions.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } @@ -392,7 +334,7 @@ public class ResourcesControllerTest extends AbstractControllerTest { Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assertions.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } @@ -410,17 +352,13 @@ public class ResourcesControllerTest extends AbstractControllerTest { Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); - Assertions.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); logger.info(mvcResult.getResponse().getContentAsString()); } @Test public void testDeleteResource() throws Exception { - Result mockResult = new Result<>(); - mockResult.setCode(Status.SUCCESS.getCode()); - Mockito.when(resourcesService.delete(Mockito.any(), Mockito.anyString(), - Mockito.anyString())) - .thenReturn(mockResult); + Mockito.doNothing().when(resourcesService).delete(Mockito.any()); MultiValueMap paramsMap = new LinkedMultiValueMap<>(); paramsMap.add("fullName", "dolphinscheduler/resourcePath"); paramsMap.add("tenantCode", "123"); @@ -431,9 +369,10 @@ public class ResourcesControllerTest extends AbstractControllerTest { .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andReturn(); - Result result = JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), Result.class); + Result result = + JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() { + }); - Assertions.assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); - logger.info(mvcResult.getResponse().getContentAsString()); + assertEquals(Status.SUCCESS.getCode(), result.getCode().intValue()); } } diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/python/PythonGatewayTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/python/PythonGatewayTest.java index 173ae98854..5ae26af826 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/python/PythonGatewayTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/python/PythonGatewayTest.java @@ -97,19 +97,6 @@ public class PythonGatewayTest { Assertions.assertEquals((long) result.get("taskDefinitionCode"), taskDefinition.getCode()); } - @Test - public void testCreateResource() { - User user = getTestUser(); - String resourceDir = "/dir1/dir2/"; - String resourceName = "test"; - String resourceSuffix = "py"; - String content = "content"; - String resourceFullName = resourceDir + resourceName + "." + resourceSuffix; - - Assertions.assertDoesNotThrow( - () -> pythonGateway.createOrUpdateResource(user.getUserName(), resourceFullName, content)); - } - @Test public void testQueryResourcesFileInfo() throws Exception { User user = getTestUser(); @@ -118,12 +105,11 @@ public class PythonGatewayTest { Mockito.when(resourcesService.queryFileStatus(user.getUserName(), storageEntity.getFullName())) .thenReturn(storageEntity); StorageEntity result = pythonGateway.queryResourcesFileInfo(user.getUserName(), storageEntity.getFullName()); - Assertions.assertEquals(result.getId(), storageEntity.getId()); + Assertions.assertEquals(result.getFullName(), storageEntity.getFullName()); } private StorageEntity getTestResource() { StorageEntity storageEntity = new StorageEntity(); - storageEntity.setId(1); storageEntity.setType(ResourceType.FILE); storageEntity.setFullName("/dev/test.py"); return storageEntity; diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ExecuteFunctionServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ExecuteFunctionServiceTest.java index 8f1869c1fb..04dabf5ead 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ExecuteFunctionServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ExecuteFunctionServiceTest.java @@ -67,6 +67,7 @@ import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper; import org.apache.dolphinscheduler.dao.mapper.TaskGroupQueueMapper; import org.apache.dolphinscheduler.dao.mapper.TenantMapper; import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType; import org.apache.dolphinscheduler.service.command.CommandService; import org.apache.dolphinscheduler.service.process.ProcessService; @@ -271,7 +272,7 @@ public class ExecuteFunctionServiceTest { null, null, null, null, null, RunMode.RUN_MODE_SERIAL, - Priority.LOW, Constants.DEFAULT_WORKER_GROUP, tenantCode, 100L, 10, null, null, + Priority.LOW, WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 10, null, null, Constants.DRY_RUN_FLAG_NO, Constants.TEST_FLAG_NO, ComplementDependentMode.OFF_MODE, null, @@ -298,7 +299,7 @@ public class ExecuteFunctionServiceTest { null, "123456789,987654321", null, null, null, RunMode.RUN_MODE_SERIAL, - Priority.LOW, Constants.DEFAULT_WORKER_GROUP, tenantCode, 100L, 110, null, null, + Priority.LOW, WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 110, null, null, Constants.DRY_RUN_FLAG_NO, Constants.TEST_FLAG_NO, ComplementDependentMode.OFF_MODE, null, @@ -323,7 +324,7 @@ public class ExecuteFunctionServiceTest { null, "1123456789,987654321", null, null, null, RunMode.RUN_MODE_SERIAL, - Priority.LOW, Constants.DEFAULT_WORKER_GROUP, tenantCode, 100L, 110, null, 0, + Priority.LOW, WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 110, null, 0, Constants.DRY_RUN_FLAG_NO, Constants.TEST_FLAG_NO, ComplementDependentMode.OFF_MODE, null, @@ -354,14 +355,14 @@ public class ExecuteFunctionServiceTest { dependentProcessDefinition.setProcessDefinitionCode(2); dependentProcessDefinition.setProcessDefinitionVersion(1); dependentProcessDefinition.setTaskDefinitionCode(1); - dependentProcessDefinition.setWorkerGroup(Constants.DEFAULT_WORKER_GROUP); + dependentProcessDefinition.setWorkerGroup(WorkerGroupUtils.getDefaultWorkerGroup()); dependentProcessDefinition.setTaskParams( "{\"localParams\":[],\"resourceList\":[],\"dependence\":{\"relation\":\"AND\",\"dependTaskList\":[{\"relation\":\"AND\",\"dependItemList\":[{\"depTaskCode\":2,\"status\":\"SUCCESS\"}]}]},\"conditionResult\":{\"successNode\":[1],\"failedNode\":[1]}}"); Mockito.when(processService.queryDependentProcessDefinitionByProcessDefinitionCode(processDefinitionCode)) .thenReturn(Lists.newArrayList(dependentProcessDefinition)); Map processDefinitionWorkerGroupMap = new HashMap<>(); - processDefinitionWorkerGroupMap.put(1L, Constants.DEFAULT_WORKER_GROUP); + processDefinitionWorkerGroupMap.put(1L, WorkerGroupUtils.getDefaultWorkerGroup()); Mockito.when(workerGroupService.queryWorkerGroupByProcessDefinitionCodes(Lists.newArrayList(1L))) .thenReturn(processDefinitionWorkerGroupMap); @@ -370,7 +371,7 @@ public class ExecuteFunctionServiceTest { command.setCommandType(CommandType.COMPLEMENT_DATA); command.setCommandParam( "{\"StartNodeList\":\"1\",\"complementStartDate\":\"2020-01-01 00:00:00\",\"complementEndDate\":\"2020-01-31 23:00:00\"}"); - command.setWorkerGroup(Constants.DEFAULT_WORKER_GROUP); + command.setWorkerGroup(WorkerGroupUtils.getDefaultWorkerGroup()); command.setProcessDefinitionCode(processDefinitionCode); command.setExecutorId(1); @@ -383,7 +384,7 @@ public class ExecuteFunctionServiceTest { childDependent.setProcessDefinitionCode(3); childDependent.setProcessDefinitionVersion(1); childDependent.setTaskDefinitionCode(4); - childDependent.setWorkerGroup(Constants.DEFAULT_WORKER_GROUP); + childDependent.setWorkerGroup(WorkerGroupUtils.getDefaultWorkerGroup()); childDependent.setTaskParams( "{\"localParams\":[],\"resourceList\":[],\"dependence\":{\"relation\":\"AND\",\"dependTaskList\":[{\"relation\":\"AND\",\"dependItemList\":[{\"depTaskCode\":3,\"status\":\"SUCCESS\"}]}]},\"conditionResult\":{\"successNode\":[1],\"failedNode\":[1]}}"); Mockito.when(processService.queryDependentProcessDefinitionByProcessDefinitionCode( @@ -409,7 +410,8 @@ public class ExecuteFunctionServiceTest { null, null, null, null, null, RunMode.RUN_MODE_SERIAL, - Priority.LOW, Constants.DEFAULT_WORKER_GROUP, tenantCode, 100L, 110, null, 2, Constants.DRY_RUN_FLAG_NO, + Priority.LOW, WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 110, null, 2, + Constants.DRY_RUN_FLAG_NO, Constants.TEST_FLAG_NO, ComplementDependentMode.OFF_MODE, null, false, @@ -434,7 +436,7 @@ public class ExecuteFunctionServiceTest { null, null, null, null, null, RunMode.RUN_MODE_SERIAL, - Priority.LOW, Constants.DEFAULT_WORKER_GROUP, tenantCode, 100L, 110, null, null, + Priority.LOW, WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 110, null, null, Constants.DRY_RUN_FLAG_NO, Constants.TEST_FLAG_NO, ComplementDependentMode.OFF_MODE, null, @@ -460,7 +462,8 @@ public class ExecuteFunctionServiceTest { null, null, null, null, null, RunMode.RUN_MODE_PARALLEL, - Priority.LOW, Constants.DEFAULT_WORKER_GROUP, tenantCode, 100L, 110, null, 2, Constants.DRY_RUN_FLAG_NO, + Priority.LOW, WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 110, null, 2, + Constants.DRY_RUN_FLAG_NO, Constants.TEST_FLAG_NO, ComplementDependentMode.OFF_MODE, null, false, @@ -486,7 +489,7 @@ public class ExecuteFunctionServiceTest { null, null, null, null, null, RunMode.RUN_MODE_PARALLEL, - Priority.LOW, Constants.DEFAULT_WORKER_GROUP, tenantCode, 100L, 110, null, 15, + Priority.LOW, WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 110, null, 15, Constants.DRY_RUN_FLAG_NO, Constants.TEST_FLAG_NO, ComplementDependentMode.OFF_MODE, null, @@ -514,7 +517,7 @@ public class ExecuteFunctionServiceTest { null, RunMode.RUN_MODE_PARALLEL, Priority.LOW, - Constants.DEFAULT_WORKER_GROUP, + WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 110, @@ -553,7 +556,7 @@ public class ExecuteFunctionServiceTest { null, null, null, null, 0, RunMode.RUN_MODE_PARALLEL, - Priority.LOW, Constants.DEFAULT_WORKER_GROUP, tenantCode, 100L, 110, null, 15, + Priority.LOW, WorkerGroupUtils.getDefaultWorkerGroup(), tenantCode, 100L, 110, null, 15, Constants.DRY_RUN_FLAG_NO, Constants.TEST_FLAG_YES, ComplementDependentMode.OFF_MODE, null, diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java index ed3a5f639b..37af53dbbd 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessDefinitionServiceTest.java @@ -72,6 +72,7 @@ import org.apache.dolphinscheduler.dao.model.PageListingResult; import org.apache.dolphinscheduler.dao.repository.ProcessDefinitionDao; import org.apache.dolphinscheduler.dao.repository.ProcessDefinitionLogDao; import org.apache.dolphinscheduler.dao.repository.TaskDefinitionLogDao; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.service.alert.ListenerEventAlertManager; import org.apache.dolphinscheduler.service.process.ProcessService; import org.apache.dolphinscheduler.spi.enums.DbType; @@ -1143,7 +1144,7 @@ public class ProcessDefinitionServiceTest extends BaseServiceTestTool { schedule.setProcessInstancePriority(Priority.MEDIUM); schedule.setWarningType(WarningType.NONE); schedule.setWarningGroupId(1); - schedule.setWorkerGroup(Constants.DEFAULT_WORKER_GROUP); + schedule.setWorkerGroup(WorkerGroupUtils.getDefaultWorkerGroup()); return schedule; } diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessInstanceServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessInstanceServiceTest.java index 7d9a4f9338..208d880fc1 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessInstanceServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ProcessInstanceServiceTest.java @@ -627,7 +627,8 @@ public class ProcessInstanceServiceTest { try ( MockedStatic taskPluginManagerMockedStatic = Mockito.mockStatic(TaskPluginManager.class)) { - taskPluginManagerMockedStatic.when(() -> TaskPluginManager.checkTaskParameters(Mockito.any())) + taskPluginManagerMockedStatic + .when(() -> TaskPluginManager.checkTaskParameters(Mockito.any(), Mockito.any())) .thenReturn(true); Map processInstanceFinishRes = processInstanceService.updateProcessInstance(loginUser, projectCode, 1, diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ResourcesServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ResourcesServiceTest.java deleted file mode 100644 index 6e94a25861..0000000000 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/ResourcesServiceTest.java +++ /dev/null @@ -1,657 +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.dolphinscheduler.api.service; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import org.apache.dolphinscheduler.api.dto.resources.DeleteDataTransferResponse; -import org.apache.dolphinscheduler.api.dto.resources.ResourceComponent; -import org.apache.dolphinscheduler.api.enums.Status; -import org.apache.dolphinscheduler.api.exceptions.ServiceException; -import org.apache.dolphinscheduler.api.permission.ResourcePermissionCheckService; -import org.apache.dolphinscheduler.api.service.impl.ResourcesServiceImpl; -import org.apache.dolphinscheduler.api.utils.PageInfo; -import org.apache.dolphinscheduler.api.utils.Result; -import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.UserType; -import org.apache.dolphinscheduler.common.utils.FileUtils; -import org.apache.dolphinscheduler.common.utils.PropertyUtils; -import org.apache.dolphinscheduler.dao.entity.Tenant; -import org.apache.dolphinscheduler.dao.entity.User; -import org.apache.dolphinscheduler.dao.mapper.ProcessDefinitionMapper; -import org.apache.dolphinscheduler.dao.mapper.TenantMapper; -import org.apache.dolphinscheduler.dao.mapper.UdfFuncMapper; -import org.apache.dolphinscheduler.dao.mapper.UserMapper; -import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.spi.enums.ResourceType; - -import org.apache.commons.collections4.CollectionUtils; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; -import org.springframework.mock.web.MockMultipartFile; - -import com.google.common.io.Files; - -/** - * resources service test - */ -@ExtendWith(MockitoExtension.class) -@MockitoSettings(strictness = Strictness.LENIENT) -public class ResourcesServiceTest { - - private static final String basePath = "/dolphinscheduler"; - private static final String tenantCode = "123"; - private static final String tenantFileResourceDir = "/dolphinscheduler/123/resources/"; - private static final String tenantUdfResourceDir = "/dolphinscheduler/123/udfs/"; - - @InjectMocks - private ResourcesServiceImpl resourcesService; - - @Mock - private TenantMapper tenantMapper; - - @Mock - private StorageOperate storageOperate; - - @Mock - private UserMapper userMapper; - - @Mock - private UdfFuncMapper udfFunctionMapper; - - @Mock - private ProcessDefinitionMapper processDefinitionMapper; - - @Mock - private ResourcePermissionCheckService resourcePermissionCheckService; - - private MockedStatic mockedStaticFileUtils; - - private MockedStatic mockedStaticFiles; - - private MockedStatic mockedStaticDolphinschedulerFileUtils; - - private MockedStatic mockedStaticPropertyUtils; - - private MockedStatic mockedStaticPaths; - - private MockedStatic filesMockedStatic; - - private Exception exception; - - @BeforeEach - public void setUp() { - mockedStaticFileUtils = Mockito.mockStatic(FileUtils.class); - mockedStaticFiles = Mockito.mockStatic(Files.class); - mockedStaticDolphinschedulerFileUtils = - Mockito.mockStatic(org.apache.dolphinscheduler.api.utils.FileUtils.class); - - mockedStaticPropertyUtils = Mockito.mockStatic(PropertyUtils.class); - mockedStaticPaths = Mockito.mockStatic(Paths.class); - filesMockedStatic = Mockito.mockStatic(java.nio.file.Files.class); - } - - @AfterEach - public void after() { - mockedStaticFileUtils.close(); - mockedStaticFiles.close(); - mockedStaticDolphinschedulerFileUtils.close(); - mockedStaticPropertyUtils.close(); - mockedStaticPaths.close(); - filesMockedStatic.close(); - } - - @Test - public void testCreateResource() { - User user = new User(); - user.setId(1); - user.setUserType(UserType.GENERAL_USER); - - // CURRENT_LOGIN_USER_TENANT_NOT_EXIST - when(userMapper.selectById(user.getId())).thenReturn(getUser()); - when(tenantMapper.queryById(1)).thenReturn(null); - ServiceException serviceException = Assertions.assertThrows(ServiceException.class, - () -> resourcesService.uploadResource(user, "ResourcesServiceTest", ResourceType.FILE, - new MockMultipartFile("test.pdf", "test.pdf", "pdf", "test".getBytes()), "/")); - assertEquals(Status.CURRENT_LOGIN_USER_TENANT_NOT_EXIST.getMsg(), serviceException.getMessage()); - - // set tenant for user - user.setTenantId(1); - when(tenantMapper.queryById(1)).thenReturn(getTenant()); - when(storageOperate.getDir(ResourceType.ALL, tenantCode)).thenReturn(basePath); - - // ILLEGAL_RESOURCE_FILE - String illegal_path = "/dolphinscheduler/123/../"; - serviceException = Assertions.assertThrows(ServiceException.class, - () -> { - MockMultipartFile mockMultipartFile = new MockMultipartFile("test.pdf", "".getBytes()); - resourcesService.uploadResource(user, "ResourcesServiceTest", ResourceType.FILE, - mockMultipartFile, illegal_path); - }); - assertEquals(new ServiceException(Status.ILLEGAL_RESOURCE_PATH, illegal_path), serviceException); - - // RESOURCE_FILE_IS_EMPTY - MockMultipartFile mockMultipartFile = new MockMultipartFile("test.pdf", "".getBytes()); - Result result = resourcesService.uploadResource(user, "ResourcesServiceTest", ResourceType.FILE, - mockMultipartFile, tenantFileResourceDir); - assertEquals(Status.RESOURCE_FILE_IS_EMPTY.getMsg(), result.getMsg()); - - // RESOURCE_SUFFIX_FORBID_CHANGE - mockMultipartFile = new MockMultipartFile("test.pdf", "test.pdf", "pdf", "test".getBytes()); - when(Files.getFileExtension("test.pdf")).thenReturn("pdf"); - when(Files.getFileExtension("ResourcesServiceTest.jar")).thenReturn("jar"); - result = resourcesService.uploadResource(user, "ResourcesServiceTest.jar", ResourceType.FILE, mockMultipartFile, - tenantFileResourceDir); - assertEquals(Status.RESOURCE_SUFFIX_FORBID_CHANGE.getMsg(), result.getMsg()); - - // UDF_RESOURCE_SUFFIX_NOT_JAR - mockMultipartFile = - new MockMultipartFile("ResourcesServiceTest.pdf", "ResourcesServiceTest.pdf", "pdf", "test".getBytes()); - when(Files.getFileExtension("ResourcesServiceTest.pdf")).thenReturn("pdf"); - result = resourcesService.uploadResource(user, "ResourcesServiceTest.pdf", ResourceType.UDF, mockMultipartFile, - tenantUdfResourceDir); - assertEquals(Status.UDF_RESOURCE_SUFFIX_NOT_JAR.getMsg(), result.getMsg()); - - // FULL_FILE_NAME_TOO_LONG - String tooLongFileName = getRandomStringWithLength(Constants.RESOURCE_FULL_NAME_MAX_LENGTH) + ".pdf"; - mockMultipartFile = new MockMultipartFile(tooLongFileName, tooLongFileName, "pdf", "test".getBytes()); - when(Files.getFileExtension(tooLongFileName)).thenReturn("pdf"); - - // '/databasePath/tenantCode/RESOURCE/' - when(storageOperate.getResDir(tenantCode)).thenReturn(tenantFileResourceDir); - result = resourcesService.uploadResource(user, tooLongFileName, ResourceType.FILE, mockMultipartFile, - tenantFileResourceDir); - assertEquals(Status.RESOURCE_FULL_NAME_TOO_LONG_ERROR.getMsg(), result.getMsg()); - } - - @Test - public void testCreateDirecotry() throws IOException { - User user = new User(); - user.setId(1); - user.setUserType(UserType.GENERAL_USER); - - String fileName = "directoryTest"; - // RESOURCE_EXIST - user.setId(1); - user.setTenantId(1); - when(tenantMapper.queryById(1)).thenReturn(getTenant()); - when(userMapper.selectById(user.getId())).thenReturn(getUser()); - when(storageOperate.getDir(ResourceType.ALL, tenantCode)).thenReturn(basePath); - when(storageOperate.getResDir(tenantCode)).thenReturn(tenantFileResourceDir); - when(storageOperate.exists(tenantFileResourceDir + fileName)).thenReturn(true); - Result result = resourcesService.createDirectory(user, fileName, ResourceType.FILE, -1, tenantFileResourceDir); - assertEquals(Status.RESOURCE_EXIST.getMsg(), result.getMsg()); - } - - @Test - public void testUpdateResource() throws Exception { - User user = new User(); - user.setId(1); - user.setUserType(UserType.GENERAL_USER); - user.setTenantId(1); - - when(userMapper.selectById(user.getId())).thenReturn(getUser()); - when(tenantMapper.queryById(1)).thenReturn(getTenant()); - when(storageOperate.getDir(ResourceType.ALL, tenantCode)).thenReturn(basePath); - when(storageOperate.getResDir(tenantCode)).thenReturn(tenantFileResourceDir); - - // TENANT_NOT_EXIST - when(tenantMapper.queryById(Mockito.anyInt())).thenReturn(null); - Assertions.assertThrows(ServiceException.class, () -> resourcesService.updateResource(user, - "ResourcesServiceTest1.jar", "", "ResourcesServiceTest", ResourceType.UDF, null)); - - // USER_NO_OPERATION_PERM - user.setUserType(UserType.GENERAL_USER); - // tenant who have access to resource is 123, - Tenant tenantWNoPermission = new Tenant(); - tenantWNoPermission.setTenantCode("321"); - when(tenantMapper.queryById(1)).thenReturn(tenantWNoPermission); - when(storageOperate.getDir(ResourceType.ALL, "321")).thenReturn(basePath); - - String fileName = "ResourcesServiceTest"; - Result result = resourcesService.updateResource(user, tenantFileResourceDir + fileName, - tenantCode, fileName, ResourceType.FILE, null); - assertEquals(Status.NO_CURRENT_OPERATING_PERMISSION.getMsg(), result.getMsg()); - - // SUCCESS - when(tenantMapper.queryById(1)).thenReturn(getTenant()); - when(storageOperate.exists(Mockito.any())).thenReturn(false); - - when(storageOperate.getDir(ResourceType.FILE, tenantCode)).thenReturn(tenantFileResourceDir); - when(storageOperate.getFileStatus(tenantFileResourceDir + fileName, - tenantFileResourceDir, tenantCode, ResourceType.FILE)) - .thenReturn(getStorageEntityResource(fileName)); - result = resourcesService.updateResource(user, tenantFileResourceDir + fileName, - tenantCode, fileName, ResourceType.FILE, null); - assertEquals(Status.SUCCESS.getMsg(), result.getMsg()); - - // Tests for udf resources. - fileName = "ResourcesServiceTest.jar"; - when(storageOperate.getDir(ResourceType.UDF, tenantCode)).thenReturn(tenantUdfResourceDir); - when(storageOperate.exists(tenantUdfResourceDir + fileName)).thenReturn(true); - when(storageOperate.getFileStatus(tenantUdfResourceDir + fileName, tenantUdfResourceDir, tenantCode, - ResourceType.UDF)) - .thenReturn(getStorageEntityUdfResource(fileName)); - result = resourcesService.updateResource(user, tenantUdfResourceDir + fileName, - tenantCode, fileName, ResourceType.UDF, null); - assertEquals(Status.SUCCESS.getMsg(), result.getMsg()); - } - - @Test - public void testQueryResourceListPaging() throws Exception { - User loginUser = new User(); - loginUser.setId(1); - loginUser.setTenantId(1); - loginUser.setTenantCode("tenant1"); - loginUser.setUserType(UserType.ADMIN_USER); - - String fileName = "ResourcesServiceTest"; - List mockResList = new ArrayList<>(); - mockResList.add(getStorageEntityResource(fileName)); - List mockUserList = new ArrayList<>(); - mockUserList.add(getUser()); - when(userMapper.selectList(null)).thenReturn(mockUserList); - when(userMapper.selectById(getUser().getId())).thenReturn(getUser()); - when(tenantMapper.queryById(getUser().getTenantId())).thenReturn(getTenant()); - when(storageOperate.getResDir(tenantCode)).thenReturn(tenantFileResourceDir); - when(storageOperate.listFilesStatus(tenantFileResourceDir, tenantFileResourceDir, - tenantCode, ResourceType.FILE)).thenReturn(mockResList); - - Result result = resourcesService.queryResourceListPaging(loginUser, "", "", ResourceType.FILE, "Test", 1, 10); - assertEquals(Status.SUCCESS.getCode(), (int) result.getCode()); - PageInfo pageInfo = (PageInfo) result.getData(); - Assertions.assertTrue(CollectionUtils.isNotEmpty(pageInfo.getTotalList())); - - } - - @Test - public void testQueryResourceList() { - User loginUser = getUser(); - String fileName = "ResourcesServiceTest"; - - when(userMapper.selectList(null)).thenReturn(Collections.singletonList(loginUser)); - when(userMapper.selectById(loginUser.getId())).thenReturn(loginUser); - when(tenantMapper.queryById(Mockito.anyInt())).thenReturn(getTenant()); - when(storageOperate.getDir(ResourceType.ALL, tenantCode)).thenReturn(basePath); - when(storageOperate.getDir(ResourceType.FILE, tenantCode)).thenReturn(tenantFileResourceDir); - when(storageOperate.getResDir(tenantCode)).thenReturn(tenantFileResourceDir); - when(storageOperate.listFilesStatusRecursively(tenantFileResourceDir, - tenantFileResourceDir, tenantCode, ResourceType.FILE)) - .thenReturn(Collections.singletonList(getStorageEntityResource(fileName))); - Map result = - resourcesService.queryResourceList(loginUser, ResourceType.FILE, tenantFileResourceDir); - assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); - List resourceList = (List) result.get(Constants.DATA_LIST); - Assertions.assertTrue(CollectionUtils.isNotEmpty(resourceList)); - - // test udf - when(storageOperate.getDir(ResourceType.UDF, tenantCode)).thenReturn(tenantUdfResourceDir); - when(storageOperate.getUdfDir(tenantCode)).thenReturn(tenantUdfResourceDir); - when(storageOperate.listFilesStatusRecursively(tenantUdfResourceDir, tenantUdfResourceDir, - tenantCode, ResourceType.UDF)).thenReturn(Arrays.asList(getStorageEntityUdfResource("test.jar"))); - loginUser.setUserType(UserType.GENERAL_USER); - result = resourcesService.queryResourceList(loginUser, ResourceType.UDF, tenantUdfResourceDir); - assertEquals(Status.SUCCESS, result.get(Constants.STATUS)); - resourceList = (List) result.get(Constants.DATA_LIST); - Assertions.assertTrue(CollectionUtils.isNotEmpty(resourceList)); - } - - @Test - public void testDelete() throws Exception { - User loginUser = new User(); - loginUser.setId(0); - loginUser.setUserType(UserType.GENERAL_USER); - - // TENANT_NOT_EXIST - loginUser.setUserType(UserType.ADMIN_USER); - loginUser.setTenantId(2); - when(userMapper.selectById(loginUser.getId())).thenReturn(loginUser); - Assertions.assertThrows(ServiceException.class, () -> resourcesService.delete(loginUser, "", "")); - - // RESOURCE_NOT_EXIST - String fileName = "ResourcesServiceTest"; - when(tenantMapper.queryById(Mockito.anyInt())).thenReturn(getTenant()); - when(storageOperate.getDir(ResourceType.ALL, tenantCode)).thenReturn(basePath); - when(storageOperate.getResDir(getTenant().getTenantCode())).thenReturn(tenantFileResourceDir); - when(storageOperate.getFileStatus(tenantFileResourceDir + fileName, tenantFileResourceDir, tenantCode, null)) - .thenReturn(getStorageEntityResource(fileName)); - Result result = resourcesService.delete(loginUser, tenantFileResourceDir + "ResNotExist", tenantCode); - assertEquals(Status.RESOURCE_NOT_EXIST.getMsg(), result.getMsg()); - - // SUCCESS - loginUser.setTenantId(1); - result = resourcesService.delete(loginUser, tenantFileResourceDir + fileName, tenantCode); - assertEquals(Status.SUCCESS.getMsg(), result.getMsg()); - } - - @Test - public void testVerifyResourceName() throws IOException { - User user = new User(); - user.setId(1); - user.setUserType(UserType.GENERAL_USER); - - String fileName = "ResourcesServiceTest"; - when(storageOperate.exists(tenantFileResourceDir + fileName)).thenReturn(true); - - Result result = resourcesService.verifyResourceName(tenantFileResourceDir + fileName, ResourceType.FILE, user); - assertEquals(Status.RESOURCE_EXIST.getMsg(), result.getMsg()); - - // RESOURCE_FILE_EXIST - result = resourcesService.verifyResourceName(tenantFileResourceDir + fileName, ResourceType.FILE, user); - Assertions.assertTrue(Status.RESOURCE_EXIST.getCode() == result.getCode()); - - // SUCCESS - result = resourcesService.verifyResourceName("test2", ResourceType.FILE, user); - assertEquals(Status.SUCCESS.getMsg(), result.getMsg()); - } - - @Test - public void testReadResource() throws IOException { - // RESOURCE_NOT_EXIST - when(userMapper.selectById(getUser().getId())).thenReturn(getUser()); - when(tenantMapper.queryById(getUser().getTenantId())).thenReturn(getTenant()); - Result result = resourcesService.readResource(getUser(), "", "", 1, 10); - assertEquals(Status.RESOURCE_FILE_NOT_EXIST.getCode(), (int) result.getCode()); - - // RESOURCE_SUFFIX_NOT_SUPPORT_VIEW - when(FileUtils.getResourceViewSuffixes()).thenReturn("class"); - result = resourcesService.readResource(getUser(), "", "", 1, 10); - assertEquals(Status.RESOURCE_SUFFIX_NOT_SUPPORT_VIEW.getMsg(), result.getMsg()); - - // USER_NOT_EXIST - when(userMapper.selectById(getUser().getId())).thenReturn(null); - when(FileUtils.getResourceViewSuffixes()).thenReturn("jar"); - when(Files.getFileExtension("ResourcesServiceTest.jar")).thenReturn("jar"); - result = resourcesService.readResource(getUser(), "", "", 1, 10); - assertEquals(Status.USER_NOT_EXIST.getCode(), (int) result.getCode()); - - // TENANT_NOT_EXIST - when(userMapper.selectById(getUser().getId())).thenReturn(getUser()); - when(tenantMapper.queryById(getUser().getTenantId())).thenReturn(null); - Assertions.assertThrows(ServiceException.class, () -> resourcesService.readResource(getUser(), "", "", 1, 10)); - - // SUCCESS - when(FileUtils.getResourceViewSuffixes()).thenReturn("jar,sh"); - when(storageOperate.getDir(ResourceType.ALL, tenantCode)).thenReturn(basePath); - when(storageOperate.getResDir(getTenant().getTenantCode())).thenReturn(tenantFileResourceDir); - when(userMapper.selectById(getUser().getId())).thenReturn(getUser()); - when(tenantMapper.queryById(getUser().getTenantId())).thenReturn(getTenant()); - when(storageOperate.exists(Mockito.any())).thenReturn(true); - when(storageOperate.vimFile(Mockito.any(), Mockito.any(), eq(1), eq(10))).thenReturn(getContent()); - when(Files.getFileExtension("/dolphinscheduler/123/resources/test.jar")).thenReturn("jar"); - result = resourcesService.readResource(getUser(), "/dolphinscheduler/123/resources/test.jar", tenantCode, 1, - 10); - assertEquals(Status.SUCCESS.getMsg(), result.getMsg()); - } - - @Test - public void testCreateOrUpdateResource() throws Exception { - User user = getUser(); - when(userMapper.queryByUserNameAccurately(user.getUserName())).thenReturn(getUser()); - - // RESOURCE_SUFFIX_NOT_SUPPORT_VIEW - exception = Assertions.assertThrows(IllegalArgumentException.class, - () -> resourcesService.createOrUpdateResource(user.getUserName(), "filename", "my-content")); - Assertions.assertTrue( - exception.getMessage().contains("Not allow create or update resources without extension name")); - - // SUCCESS - String fileName = "ResourcesServiceTest"; - when(storageOperate.getResDir(user.getTenantCode())).thenReturn(tenantFileResourceDir); - when(FileUtils.getUploadFilename(Mockito.anyString(), Mockito.anyString())).thenReturn("test"); - when(FileUtils.writeContent2File(Mockito.anyString(), Mockito.anyString())).thenReturn(true); - when(storageOperate.getFileStatus(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any())) - .thenReturn(getStorageEntityResource(fileName)); - StorageEntity storageEntity = - resourcesService.createOrUpdateResource(user.getUserName(), "filename.txt", "my-content"); - Assertions.assertNotNull(storageEntity); - assertEquals(tenantFileResourceDir + fileName, storageEntity.getFullName()); - } - - @Test - public void testUpdateResourceContent() throws Exception { - // RESOURCE_PATH_ILLEGAL - when(userMapper.selectById(getUser().getId())).thenReturn(getUser()); - when(tenantMapper.queryById(1)).thenReturn(getTenant()); - when(storageOperate.getResDir(Mockito.anyString())).thenReturn("/tmp"); - - String fileName = "ResourcesServiceTest.jar"; - ServiceException serviceException = - Assertions.assertThrows(ServiceException.class, () -> resourcesService.updateResourceContent(getUser(), - tenantFileResourceDir + fileName, tenantCode, "content")); - assertEquals(new ServiceException(Status.ILLEGAL_RESOURCE_PATH, tenantFileResourceDir + fileName), - serviceException); - - // RESOURCE_NOT_EXIST - when(storageOperate.getDir(ResourceType.ALL, tenantCode)).thenReturn(basePath); - when(storageOperate.getResDir(Mockito.anyString())).thenReturn(tenantFileResourceDir); - when(storageOperate.getFileStatus(tenantFileResourceDir + fileName, "", tenantCode, ResourceType.FILE)) - .thenReturn(null); - Result result = resourcesService.updateResourceContent(getUser(), tenantFileResourceDir + fileName, tenantCode, - "content"); - assertEquals(Status.RESOURCE_NOT_EXIST.getMsg(), result.getMsg()); - - // RESOURCE_SUFFIX_NOT_SUPPORT_VIEW - when(FileUtils.getResourceViewSuffixes()).thenReturn("class"); - when(storageOperate.getFileStatus(tenantFileResourceDir, "", tenantCode, ResourceType.FILE)) - .thenReturn(getStorageEntityResource(fileName)); - - result = resourcesService.updateResourceContent(getUser(), tenantFileResourceDir, tenantCode, - "content"); - assertEquals(Status.RESOURCE_SUFFIX_NOT_SUPPORT_VIEW.getMsg(), result.getMsg()); - - // USER_NOT_EXIST - when(userMapper.selectById(getUser().getId())).thenReturn(null); - result = resourcesService.updateResourceContent(getUser(), tenantFileResourceDir + "123.class", - tenantCode, - "content"); - Assertions.assertTrue(Status.USER_NOT_EXIST.getCode() == result.getCode()); - - // TENANT_NOT_EXIST - when(userMapper.selectById(getUser().getId())).thenReturn(getUser()); - when(tenantMapper.queryById(1)).thenReturn(null); - Assertions.assertThrows(ServiceException.class, () -> resourcesService.updateResourceContent(getUser(), - tenantFileResourceDir + fileName, tenantCode, "content")); - - // SUCCESS - when(storageOperate.getFileStatus(tenantFileResourceDir + fileName, "", tenantCode, - ResourceType.FILE)).thenReturn(getStorageEntityResource(fileName)); - - when(Files.getFileExtension(Mockito.anyString())).thenReturn("jar"); - when(FileUtils.getResourceViewSuffixes()).thenReturn("jar"); - when(userMapper.selectById(getUser().getId())).thenReturn(getUser()); - when(tenantMapper.queryById(1)).thenReturn(getTenant()); - when(FileUtils.getUploadFilename(Mockito.anyString(), Mockito.anyString())).thenReturn("test"); - when(FileUtils.writeContent2File(Mockito.anyString(), Mockito.anyString())).thenReturn(true); - result = resourcesService.updateResourceContent(getUser(), - tenantFileResourceDir + fileName, tenantCode, "content"); - assertEquals(Status.SUCCESS.getMsg(), result.getMsg()); - } - - @Test - public void testDownloadResource() throws IOException { - when(tenantMapper.queryById(1)).thenReturn(getTenant()); - when(userMapper.selectById(1)).thenReturn(getUser()); - org.springframework.core.io.Resource resourceMock = Mockito.mock(org.springframework.core.io.Resource.class); - Path path = Mockito.mock(Path.class); - when(Paths.get(Mockito.any())).thenReturn(path); - when(java.nio.file.Files.size(Mockito.any())).thenReturn(1L); - // resource null - org.springframework.core.io.Resource resource = resourcesService.downloadResource(getUser(), ""); - Assertions.assertNull(resource); - - when(org.apache.dolphinscheduler.api.utils.FileUtils.file2Resource(Mockito.any())).thenReturn(resourceMock); - resource = resourcesService.downloadResource(getUser(), ""); - Assertions.assertNotNull(resource); - } - - @Test - public void testDeleteDataTransferData() throws Exception { - User user = getUser(); - when(userMapper.selectById(user.getId())).thenReturn(getUser()); - when(tenantMapper.queryById(user.getTenantId())).thenReturn(getTenant()); - - StorageEntity storageEntity1 = Mockito.mock(StorageEntity.class); - StorageEntity storageEntity2 = Mockito.mock(StorageEntity.class); - StorageEntity storageEntity3 = Mockito.mock(StorageEntity.class); - StorageEntity storageEntity4 = Mockito.mock(StorageEntity.class); - StorageEntity storageEntity5 = Mockito.mock(StorageEntity.class); - - when(storageEntity1.getFullName()).thenReturn("DATA_TRANSFER/20220101"); - when(storageEntity2.getFullName()).thenReturn("DATA_TRANSFER/20220102"); - when(storageEntity3.getFullName()).thenReturn("DATA_TRANSFER/20220103"); - when(storageEntity4.getFullName()).thenReturn("DATA_TRANSFER/20220104"); - when(storageEntity5.getFullName()).thenReturn("DATA_TRANSFER/20220105"); - - List storageEntityList = new ArrayList<>(); - storageEntityList.add(storageEntity1); - storageEntityList.add(storageEntity2); - storageEntityList.add(storageEntity3); - storageEntityList.add(storageEntity4); - storageEntityList.add(storageEntity5); - - when(storageOperate.listFilesStatus(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) - .thenReturn(storageEntityList); - - LocalDateTime localDateTime = LocalDateTime.of(2022, 1, 5, 0, 0, 0); - try (MockedStatic mockHook = Mockito.mockStatic(LocalDateTime.class)) { - mockHook.when(LocalDateTime::now).thenReturn(localDateTime); - DeleteDataTransferResponse response = resourcesService.deleteDataTransferData(user, 3); - - assertEquals(response.getSuccessList().size(), 2); - assertEquals(response.getSuccessList().get(0), "DATA_TRANSFER/20220101"); - assertEquals(response.getSuccessList().get(1), "DATA_TRANSFER/20220102"); - } - - try (MockedStatic mockHook = Mockito.mockStatic(LocalDateTime.class)) { - mockHook.when(LocalDateTime::now).thenReturn(localDateTime); - DeleteDataTransferResponse response = resourcesService.deleteDataTransferData(user, 0); - assertEquals(response.getSuccessList().size(), 5); - } - - } - - @Test - public void testCatFile() throws IOException { - // SUCCESS - List list = storageOperate.vimFile(Mockito.any(), Mockito.anyString(), eq(1), eq(10)); - Assertions.assertNotNull(list); - } - - @Test - void testQueryBaseDir() throws Exception { - User user = getUser(); - String fileName = "ResourcesServiceTest.jar"; - when(userMapper.selectById(user.getId())).thenReturn(getUser()); - when(tenantMapper.queryById(user.getTenantId())).thenReturn(getTenant()); - when(storageOperate.getDir(ResourceType.FILE, tenantCode)).thenReturn(tenantFileResourceDir); - when(storageOperate.getFileStatus(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), - Mockito.any())).thenReturn(getStorageEntityResource(fileName)); - Result result = resourcesService.queryResourceBaseDir(user, ResourceType.FILE); - assertEquals(Status.SUCCESS.getMsg(), result.getMsg()); - } - - private Tenant getTenant() { - Tenant tenant = new Tenant(); - tenant.setTenantCode(tenantCode); - return tenant; - } - - private User getUser() { - User user = new User(); - user.setId(1); - user.setUserType(UserType.GENERAL_USER); - user.setTenantId(1); - user.setTenantCode(tenantCode); - return user; - } - - private StorageEntity getStorageEntityResource(String fileName) { - StorageEntity entity = new StorageEntity(); - entity.setAlias(fileName); - entity.setFileName(fileName); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(ResourceType.FILE); - entity.setFullName(tenantFileResourceDir + fileName); - return entity; - } - - private StorageEntity getStorageEntityUdfResource(String fileName) { - StorageEntity entity = new StorageEntity(); - entity.setAlias(fileName); - entity.setFileName(fileName); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(ResourceType.UDF); - entity.setFullName(tenantUdfResourceDir + fileName); - - return entity; - } - - private List getContent() { - List contentList = new ArrayList<>(); - contentList.add("test"); - return contentList; - } - - private List> getResources() { - List> resources = new ArrayList<>(); - Map resource = new HashMap<>(); - resource.put("id", 1); - resource.put("resource_ids", "1"); - resources.add(resource); - return resources; - } - - private static String getRandomStringWithLength(int length) { - Random r = new Random(); - StringBuilder sb = new StringBuilder(); - while (sb.length() < length) { - char c = (char) (r.nextInt(26) + 'a'); - sb.append(c); - } - return sb.toString(); - } -} diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TaskDefinitionServiceImplTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TaskDefinitionServiceImplTest.java index ff8ca6ba2c..8460c59924 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TaskDefinitionServiceImplTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TaskDefinitionServiceImplTest.java @@ -64,6 +64,7 @@ import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionLogMapper; import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper; import org.apache.dolphinscheduler.dao.repository.ProcessTaskRelationLogDao; import org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager; +import org.apache.dolphinscheduler.plugin.task.shell.ShellTaskChannelFactory; import org.apache.dolphinscheduler.service.process.ProcessService; import org.apache.dolphinscheduler.service.process.ProcessServiceImpl; @@ -165,7 +166,8 @@ public class TaskDefinitionServiceImplTest { try ( MockedStatic taskPluginManagerMockedStatic = Mockito.mockStatic(TaskPluginManager.class)) { - taskPluginManagerMockedStatic.when(() -> TaskPluginManager.checkTaskParameters(Mockito.any())) + taskPluginManagerMockedStatic + .when(() -> TaskPluginManager.checkTaskParameters(Mockito.any(), Mockito.any())) .thenReturn(true); Project project = getProject(); when(projectMapper.queryByCode(PROJECT_CODE)).thenReturn(project); @@ -194,7 +196,8 @@ public class TaskDefinitionServiceImplTest { try ( MockedStatic taskPluginManagerMockedStatic = Mockito.mockStatic(TaskPluginManager.class)) { - taskPluginManagerMockedStatic.when(() -> TaskPluginManager.checkTaskParameters(Mockito.any())) + taskPluginManagerMockedStatic + .when(() -> TaskPluginManager.checkTaskParameters(Mockito.any(), Mockito.any())) .thenReturn(true); String taskDefinitionJson = getTaskDefinitionJson(); @@ -441,7 +444,8 @@ public class TaskDefinitionServiceImplTest { () -> taskDefinitionService.createTaskDefinitionV2(user, taskCreateRequest)); // error task definition - taskCreateRequest.setTaskParams(TASK_PARAMETER); + taskCreateRequest.setTaskType(ShellTaskChannelFactory.NAME); + taskCreateRequest.setTaskParams(JSONUtils.toJsonString(new HashMap<>())); doNothing().when(projectService).checkProjectAndAuthThrowException(user, getProject(), TASK_DEFINITION_CREATE); assertThrowsServiceException(Status.PROCESS_NODE_S_PARAMETER_INVALID, () -> taskDefinitionService.createTaskDefinitionV2(user, taskCreateRequest)); @@ -449,7 +453,8 @@ public class TaskDefinitionServiceImplTest { try ( MockedStatic taskPluginManagerMockedStatic = Mockito.mockStatic(TaskPluginManager.class)) { - taskPluginManagerMockedStatic.when(() -> TaskPluginManager.checkTaskParameters(Mockito.any())) + taskPluginManagerMockedStatic + .when(() -> TaskPluginManager.checkTaskParameters(Mockito.any(), Mockito.any())) .thenReturn(true); // error create task definition object @@ -502,7 +507,8 @@ public class TaskDefinitionServiceImplTest { try ( MockedStatic taskPluginManagerMockedStatic = Mockito.mockStatic(TaskPluginManager.class)) { - taskPluginManagerMockedStatic.when(() -> TaskPluginManager.checkTaskParameters(Mockito.any())) + taskPluginManagerMockedStatic + .when(() -> TaskPluginManager.checkTaskParameters(Mockito.any(), Mockito.any())) .thenReturn(false); assertThrowsServiceException(Status.PROCESS_NODE_S_PARAMETER_INVALID, () -> taskDefinitionService.updateTaskDefinitionV2(user, TASK_CODE, taskUpdateRequest)); @@ -511,7 +517,8 @@ public class TaskDefinitionServiceImplTest { try ( MockedStatic taskPluginManagerMockedStatic = Mockito.mockStatic(TaskPluginManager.class)) { - taskPluginManagerMockedStatic.when(() -> TaskPluginManager.checkTaskParameters(Mockito.any())) + taskPluginManagerMockedStatic + .when(() -> TaskPluginManager.checkTaskParameters(Mockito.any(), Mockito.any())) .thenReturn(true); // error task definition nothing update when(processService.isTaskOnline(TASK_CODE)).thenReturn(false); @@ -616,7 +623,8 @@ public class TaskDefinitionServiceImplTest { try ( MockedStatic taskPluginManagerMockedStatic = Mockito.mockStatic(TaskPluginManager.class)) { - taskPluginManagerMockedStatic.when(() -> TaskPluginManager.checkTaskParameters(Mockito.any())) + taskPluginManagerMockedStatic + .when(() -> TaskPluginManager.checkTaskParameters(Mockito.any(), Mockito.any())) .thenReturn(true); String taskDefinitionJson = getTaskDefinitionJson(); TaskDefinition taskDefinition = getTaskDefinition(); diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TenantServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TenantServiceTest.java index 5746840e90..6925441b10 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TenantServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/TenantServiceTest.java @@ -41,7 +41,7 @@ import org.apache.dolphinscheduler.dao.mapper.ProcessInstanceMapper; import org.apache.dolphinscheduler.dao.mapper.ScheduleMapper; import org.apache.dolphinscheduler.dao.mapper.TenantMapper; import org.apache.dolphinscheduler.dao.mapper.UserMapper; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.collections4.CollectionUtils; @@ -95,7 +95,7 @@ public class TenantServiceTest { private ResourcePermissionCheckService resourcePermissionCheckService; @Mock - private StorageOperate storageOperate; + private StorageOperator storageOperator; private static final String tenantCode = "hayden"; private static final String tenantDesc = "This is the tenant desc"; diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UdfFuncServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UdfFuncServiceTest.java index f1721cbb46..5c7bba5821 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UdfFuncServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UdfFuncServiceTest.java @@ -32,11 +32,10 @@ import org.apache.dolphinscheduler.dao.entity.UdfFunc; import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.mapper.UDFUserMapper; import org.apache.dolphinscheduler.dao.mapper.UdfFuncMapper; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.collections4.CollectionUtils; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -83,7 +82,7 @@ public class UdfFuncServiceTest { private UDFUserMapper udfUserMapper; @Mock - private StorageOperate storageOperate; + private StorageOperator storageOperator; @BeforeEach public void setUp() { @@ -111,11 +110,7 @@ public class UdfFuncServiceTest { logger.info(result.toString()); Assertions.assertEquals(Status.RESOURCE_NOT_EXIST.getMsg(), result.getMsg()); // success - try { - Mockito.when(storageOperate.exists("String")).thenReturn(true); - } catch (IOException e) { - logger.error("AmazonServiceException when checking resource: String"); - } + Mockito.when(storageOperator.exists("String")).thenReturn(true); result = udfFuncService.createUdfFunction(getLoginUser(), "UdfFuncServiceTest", "org.apache.dolphinscheduler.api.service.UdfFuncServiceTest", "String", @@ -176,11 +171,7 @@ public class UdfFuncServiceTest { // success Mockito.when(resourcePermissionCheckService.operationPermissionCheck(AuthorizationType.UDF, 1, ApiFuncIdentificationConstant.UDF_FUNCTION_UPDATE, serviceLogger)).thenReturn(true); - try { - Mockito.when(storageOperate.exists("")).thenReturn(true); - } catch (IOException e) { - logger.error("AmazonServiceException when checking resource: "); - } + Mockito.when(storageOperator.exists("")).thenReturn(true); result = udfFuncService.updateUdfFunc(getLoginUser(), 11, "UdfFuncServiceTest", "org.apache.dolphinscheduler.api.service.UdfFuncServiceTest", "String", diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java index 3cb71d97a0..9e2a359667 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/UsersServiceTest.java @@ -48,7 +48,7 @@ import org.apache.dolphinscheduler.dao.mapper.ProjectUserMapper; import org.apache.dolphinscheduler.dao.mapper.TenantMapper; import org.apache.dolphinscheduler.dao.mapper.UDFUserMapper; import org.apache.dolphinscheduler.dao.mapper.UserMapper; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.collections4.CollectionUtils; @@ -117,7 +117,7 @@ public class UsersServiceTest { private ProjectMapper projectMapper; @Mock - private StorageOperate storageOperate; + private StorageOperator storageOperator; @Mock private ResourcePermissionCheckService resourcePermissionCheckService; diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkerGroupServiceTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkerGroupServiceTest.java index 08a541c5bb..fce9aa3f1c 100644 --- a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkerGroupServiceTest.java +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkerGroupServiceTest.java @@ -30,7 +30,6 @@ import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.AuthorizationType; import org.apache.dolphinscheduler.common.enums.UserType; import org.apache.dolphinscheduler.dao.entity.ProcessInstance; -import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.entity.WorkerGroup; import org.apache.dolphinscheduler.dao.mapper.EnvironmentWorkerGroupRelationMapper; @@ -65,8 +64,6 @@ import org.slf4j.LoggerFactory; @MockitoSettings(strictness = Strictness.LENIENT) public class WorkerGroupServiceTest { - private static final Logger logger = LoggerFactory.getLogger(WorkerGroupServiceTest.class); - private static final Logger baseServiceLogger = LoggerFactory.getLogger(BaseServiceImpl.class); private static final Logger serviceLogger = LoggerFactory.getLogger(WorkerGroupService.class); @@ -288,47 +285,6 @@ public class WorkerGroupServiceTest { Assertions.assertEquals("default", workerGroups.toArray()[0]); } - @Test - public void giveNull_whenGetTaskWorkerGroup_expectNull() { - String nullWorkerGroup = workerGroupService.getTaskWorkerGroup(null); - Assertions.assertNull(nullWorkerGroup); - } - - @Test - public void giveCorrectTaskInstance_whenGetTaskWorkerGroup_expectTaskWorkerGroup() { - TaskInstance taskInstance = new TaskInstance(); - taskInstance.setId(1); - taskInstance.setWorkerGroup("cluster1"); - - String workerGroup = workerGroupService.getTaskWorkerGroup(taskInstance); - Assertions.assertEquals("cluster1", workerGroup); - } - - @Test - public void giveNullWorkerGroup_whenGetTaskWorkerGroup_expectProcessWorkerGroup() { - TaskInstance taskInstance = new TaskInstance(); - taskInstance.setId(1); - taskInstance.setProcessInstanceId(1); - ProcessInstance processInstance = new ProcessInstance(); - processInstance.setId(1); - processInstance.setWorkerGroup("cluster1"); - Mockito.when(processService.findProcessInstanceById(1)).thenReturn(processInstance); - - String workerGroup = workerGroupService.getTaskWorkerGroup(taskInstance); - Assertions.assertEquals("cluster1", workerGroup); - } - - @Test - public void giveNullTaskAndProcessWorkerGroup_whenGetTaskWorkerGroup_expectDefault() { - TaskInstance taskInstance = new TaskInstance(); - taskInstance.setId(1); - taskInstance.setProcessInstanceId(1); - Mockito.when(processService.findProcessInstanceById(1)).thenReturn(null); - - String defaultWorkerGroup = workerGroupService.getTaskWorkerGroup(taskInstance); - Assertions.assertEquals(Constants.DEFAULT_WORKER_GROUP, defaultWorkerGroup); - } - /** * get Group */ diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryDtoValidatorTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryDtoValidatorTest.java new file mode 100644 index 0000000000..11803cba92 --- /dev/null +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/CreateDirectoryDtoValidatorTest.java @@ -0,0 +1,136 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import static org.apache.dolphinscheduler.api.AssertionsHelper.assertThrowServiceException; +import static org.mockito.Mockito.when; + +import org.apache.dolphinscheduler.api.dto.resources.CreateDirectoryDto; +import org.apache.dolphinscheduler.common.enums.UserType; +import org.apache.dolphinscheduler.dao.entity.Tenant; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import java.util.Locale; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.i18n.LocaleContextHolder; + +@ExtendWith(MockitoExtension.class) +class CreateDirectoryDtoValidatorTest { + + @Mock + private StorageOperator storageOperator; + + @Mock + private TenantDao tenantDao; + + @InjectMocks + private CreateDirectoryDtoValidator createDirectoryDtoValidator; + + private static final String BASE_DIRECTORY = "/tmp/dolphinscheduler"; + + private User loginUser; + + @BeforeEach + public void setup() { + when(storageOperator.getStorageBaseDirectory()).thenReturn(BASE_DIRECTORY); + loginUser = new User(); + loginUser.setTenantId(1); + LocaleContextHolder.setLocale(Locale.ENGLISH); + } + + @Test + void testValidate_notUnderBaseDirectory() { + CreateDirectoryDto createDirectoryDto = CreateDirectoryDto.builder() + .loginUser(loginUser) + .directoryAbsolutePath("/tmp") + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp", + () -> createDirectoryDtoValidator.validate(createDirectoryDto)); + } + + @Test + public void testValidate_directoryPathContainsIllegalSymbolic() { + CreateDirectoryDto createDirectoryDto = CreateDirectoryDto.builder() + .loginUser(loginUser) + .directoryAbsolutePath("/tmp/dolphinscheduler/default/resources/..") + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp/dolphinscheduler/default/resources/..", + () -> createDirectoryDtoValidator.validate(createDirectoryDto)); + } + + @Test + public void testValidate_directoryExist() { + CreateDirectoryDto createDirectoryDto = CreateDirectoryDto.builder() + .loginUser(loginUser) + .directoryAbsolutePath("/tmp/dolphinscheduler/default/resources/demo") + .build(); + when(storageOperator.exists(createDirectoryDto.getDirectoryAbsolutePath())).thenReturn(true); + assertThrowServiceException( + "Internal Server Error: The resource is already exist: /tmp/dolphinscheduler/default/resources/demo", + () -> createDirectoryDtoValidator.validate(createDirectoryDto)); + } + + @Test + public void testValidate_NoPermission() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("test"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + CreateDirectoryDto createDirectoryDto = CreateDirectoryDto.builder() + .loginUser(loginUser) + .directoryAbsolutePath("/tmp/dolphinscheduler/default/resources/demo") + .build(); + when(storageOperator.getResourceMetaData(createDirectoryDto.getDirectoryAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(createDirectoryDto.getDirectoryAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("demo") + .isDirectory(true) + .tenant("default") + .build()); + when(storageOperator.exists(createDirectoryDto.getDirectoryAbsolutePath())).thenReturn(false); + assertThrowServiceException( + "Internal Server Error: The user's tenant is test have no permission to access the resource: /tmp/dolphinscheduler/default/resources/demo", + () -> createDirectoryDtoValidator.validate(createDirectoryDto)); + } + + @Test + public void testValidate_pathNotDirectory() { + CreateDirectoryDto createDirectoryDto = CreateDirectoryDto.builder() + .loginUser(loginUser) + .directoryAbsolutePath("/tmp/dolphinscheduler/default/resources/demo.sql") + .build(); + loginUser.setUserType(UserType.ADMIN_USER); + assertThrowServiceException( + "Internal Server Error: The path is not a directory: /tmp/dolphinscheduler/default/resources/demo.sql", + () -> createDirectoryDtoValidator.validate(createDirectoryDto)); + } + +} diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileFromContentDtoValidatorTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileFromContentDtoValidatorTest.java new file mode 100644 index 0000000000..6312346e40 --- /dev/null +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/CreateFileFromContentDtoValidatorTest.java @@ -0,0 +1,188 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import static org.apache.dolphinscheduler.api.AssertionsHelper.assertDoesNotThrow; +import static org.apache.dolphinscheduler.api.AssertionsHelper.assertThrowServiceException; +import static org.mockito.Mockito.when; + +import org.apache.dolphinscheduler.api.dto.resources.CreateFileFromContentDto; +import org.apache.dolphinscheduler.dao.entity.Tenant; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import java.util.Locale; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.i18n.LocaleContextHolder; + +@ExtendWith(MockitoExtension.class) +class CreateFileFromContentDtoValidatorTest { + + @Mock + private StorageOperator storageOperator; + + @Mock + private TenantDao tenantDao; + + @InjectMocks + private CreateFileFromContentDtoValidator createFileFromContentDtoValidator; + + private static final String BASE_DIRECTORY = "/tmp/dolphinscheduler"; + + private User loginUser; + + @BeforeEach + public void setup() { + when(storageOperator.getStorageBaseDirectory()).thenReturn(BASE_DIRECTORY); + loginUser = new User(); + loginUser.setTenantId(1); + LocaleContextHolder.setLocale(Locale.ENGLISH); + } + + @Test + void testValidate_notUnderBaseDirectory() { + CreateFileFromContentDto createFileFromContentDto = CreateFileFromContentDto.builder() + .loginUser(loginUser) + .fileAbsolutePath("/tmp") + .fileContent("select * from t") + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp", + () -> createFileFromContentDtoValidator.validate(createFileFromContentDto)); + + } + + @Test + public void testValidate_filePathContainsIllegalSymbolic() { + CreateFileFromContentDto renameDirectoryDto = CreateFileFromContentDto.builder() + .loginUser(loginUser) + .fileAbsolutePath("/tmp/dolphinscheduler/default/resources/..") + .fileContent("select * from t") + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp/dolphinscheduler/default/resources/..", + () -> createFileFromContentDtoValidator.validate(renameDirectoryDto)); + } + + @Test + public void testValidate_IsNotFile() { + CreateFileFromContentDto createFileFromContentDto = CreateFileFromContentDto.builder() + .loginUser(loginUser) + .fileAbsolutePath("/tmp/dolphinscheduler/default/resources/a") + .fileContent("select * from t") + .build(); + assertThrowServiceException( + "Internal Server Error: The path is not a file: /tmp/dolphinscheduler/default/resources/a", + () -> createFileFromContentDtoValidator.validate(createFileFromContentDto)); + } + + @Test + public void testValidate_fileAlreadyExist() { + CreateFileFromContentDto createFileFromContentDto = CreateFileFromContentDto.builder() + .loginUser(loginUser) + .fileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.sql") + .fileContent("select * from t") + .build(); + when(storageOperator.exists(createFileFromContentDto.getFileAbsolutePath())).thenReturn(true); + assertThrowServiceException( + "Internal Server Error: The resource is already exist: /tmp/dolphinscheduler/default/resources/a.sql", + () -> createFileFromContentDtoValidator.validate(createFileFromContentDto)); + } + + @Test + public void testValidate_fileNoPermission() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("test"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + CreateFileFromContentDto createFileFromContentDto = CreateFileFromContentDto.builder() + .loginUser(loginUser) + .fileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.sql") + .fileContent("select * from t") + .build(); + when(storageOperator.exists(createFileFromContentDto.getFileAbsolutePath())).thenReturn(false); + when(storageOperator.getResourceMetaData(createFileFromContentDto.getFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(createFileFromContentDto.getFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.sql") + .isDirectory(false) + .tenant("default") + .build()); + assertThrowServiceException( + "Internal Server Error: The user's tenant is test have no permission to access the resource: /tmp/dolphinscheduler/default/resources/a.sql", + () -> createFileFromContentDtoValidator.validate(createFileFromContentDto)); + } + + @Test + public void testValidate_contentIsInvalidated() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("default"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + CreateFileFromContentDto createFileFromContentDto = CreateFileFromContentDto.builder() + .loginUser(loginUser) + .fileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.sql") + .fileContent("") + .build(); + when(storageOperator.exists(createFileFromContentDto.getFileAbsolutePath())).thenReturn(false); + when(storageOperator.getResourceMetaData(createFileFromContentDto.getFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(createFileFromContentDto.getFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.sql") + .isDirectory(false) + .tenant("default") + .build()); + assertThrowServiceException( + "Internal Server Error: The file content is null", + () -> createFileFromContentDtoValidator.validate(createFileFromContentDto)); + } + + @Test + public void testValidate() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("default"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + CreateFileFromContentDto createFileFromContentDto = CreateFileFromContentDto.builder() + .loginUser(loginUser) + .fileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.sql") + .fileContent("select * from t") + .build(); + when(storageOperator.exists(createFileFromContentDto.getFileAbsolutePath())).thenReturn(false); + when(storageOperator.getResourceMetaData(createFileFromContentDto.getFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(createFileFromContentDto.getFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.sql") + .isDirectory(false) + .tenant("default") + .build()); + assertDoesNotThrow(() -> createFileFromContentDtoValidator.validate(createFileFromContentDto)); + } +} diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/FetchFileContentDtoValidatorTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/FetchFileContentDtoValidatorTest.java new file mode 100644 index 0000000000..69c31f2a0e --- /dev/null +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/FetchFileContentDtoValidatorTest.java @@ -0,0 +1,197 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import static org.apache.dolphinscheduler.api.AssertionsHelper.assertThrowServiceException; +import static org.mockito.Mockito.when; + +import org.apache.dolphinscheduler.api.AssertionsHelper; +import org.apache.dolphinscheduler.api.dto.resources.FetchFileContentDto; +import org.apache.dolphinscheduler.dao.entity.Tenant; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import java.util.Locale; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.springframework.context.i18n.LocaleContextHolder; + +@MockitoSettings(strictness = Strictness.LENIENT) +@ExtendWith(MockitoExtension.class) +class FetchFileContentDtoValidatorTest { + + @Mock + private StorageOperator storageOperator; + + @Mock + private TenantDao tenantDao; + + @InjectMocks + private FetchFileContentDtoValidator fetchFileContentDtoValidator; + + private static final String BASE_DIRECTORY = "/tmp/dolphinscheduler"; + + private User loginUser; + + @BeforeEach + public void setup() { + when(storageOperator.getStorageBaseDirectory()).thenReturn(BASE_DIRECTORY); + loginUser = new User(); + loginUser.setTenantId(1); + LocaleContextHolder.setLocale(Locale.ENGLISH); + } + + @Test + void testValidate_skipLineNumInvalid() { + FetchFileContentDto fetchFileContentDto = FetchFileContentDto.builder() + .loginUser(loginUser) + .resourceFileAbsolutePath("/tmp") + .skipLineNum(-1) + .limit(-1) + .build(); + assertThrowServiceException( + "Internal Server Error: skipLineNum must be greater than or equal to 0", + () -> fetchFileContentDtoValidator.validate(fetchFileContentDto)); + + } + + @Test + void testValidate_notUnderBaseDirectory() { + FetchFileContentDto fetchFileContentDto = FetchFileContentDto.builder() + .loginUser(loginUser) + .resourceFileAbsolutePath("/tmp") + .skipLineNum(0) + .limit(-1) + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp", + () -> fetchFileContentDtoValidator.validate(fetchFileContentDto)); + + } + + @Test + public void testValidate_filePathContainsIllegalSymbolic() { + FetchFileContentDto fetchFileContentDto = FetchFileContentDto.builder() + .loginUser(loginUser) + .resourceFileAbsolutePath("/tmp/dolphinscheduler/default/resources/..") + .skipLineNum(0) + .limit(-1) + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp/dolphinscheduler/default/resources/..", + () -> fetchFileContentDtoValidator.validate(fetchFileContentDto)); + } + + @Test + public void testValidate_IsNotFile() { + FetchFileContentDto fetchFileContentDto = FetchFileContentDto.builder() + .loginUser(loginUser) + .resourceFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a") + .skipLineNum(0) + .limit(-1) + .build(); + assertThrowServiceException( + "Internal Server Error: The path is not a file: /tmp/dolphinscheduler/default/resources/a", + () -> fetchFileContentDtoValidator.validate(fetchFileContentDto)); + } + + @Test + public void testValidate_fileNoPermission() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("test"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + FetchFileContentDto fetchFileContentDto = FetchFileContentDto.builder() + .loginUser(loginUser) + .resourceFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.sql") + .skipLineNum(0) + .limit(-1) + .build(); + when(storageOperator.exists(fetchFileContentDto.getResourceFileAbsolutePath())).thenReturn(false); + when(storageOperator.getResourceMetaData(fetchFileContentDto.getResourceFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(fetchFileContentDto.getResourceFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.sql") + .isDirectory(false) + .tenant("default") + .build()); + assertThrowServiceException( + "Internal Server Error: The user's tenant is test have no permission to access the resource: /tmp/dolphinscheduler/default/resources/a.sql", + () -> fetchFileContentDtoValidator.validate(fetchFileContentDto)); + } + + @Test + void validate_fileExtensionInvalid() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("default"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + FetchFileContentDto fetchFileContentDto = FetchFileContentDto.builder() + .loginUser(loginUser) + .resourceFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.jar") + .skipLineNum(0) + .limit(-1) + .build(); + when(storageOperator.exists(fetchFileContentDto.getResourceFileAbsolutePath())).thenReturn(false); + when(storageOperator.getResourceMetaData(fetchFileContentDto.getResourceFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(fetchFileContentDto.getResourceFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.jar") + .isDirectory(false) + .tenant("default") + .build()); + assertThrowServiceException( + "Internal Server Error: The file type: jar cannot be fetched", + () -> fetchFileContentDtoValidator.validate(fetchFileContentDto)); + } + + @Test + void validate() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("default"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + FetchFileContentDto fetchFileContentDto = FetchFileContentDto.builder() + .loginUser(loginUser) + .resourceFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.sql") + .skipLineNum(0) + .limit(-1) + .build(); + when(storageOperator.exists(fetchFileContentDto.getResourceFileAbsolutePath())).thenReturn(false); + when(storageOperator.getResourceMetaData(fetchFileContentDto.getResourceFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(fetchFileContentDto.getResourceFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.sql") + .isDirectory(false) + .tenant("default") + .build()); + AssertionsHelper.assertDoesNotThrow(() -> fetchFileContentDtoValidator.validate(fetchFileContentDto)); + } +} diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryDtoValidatorTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryDtoValidatorTest.java new file mode 100644 index 0000000000..43e73f3b60 --- /dev/null +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/RenameDirectoryDtoValidatorTest.java @@ -0,0 +1,188 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import static org.apache.dolphinscheduler.api.AssertionsHelper.assertDoesNotThrow; +import static org.apache.dolphinscheduler.api.AssertionsHelper.assertThrowServiceException; +import static org.mockito.Mockito.when; + +import org.apache.dolphinscheduler.api.dto.resources.RenameDirectoryDto; +import org.apache.dolphinscheduler.dao.entity.Tenant; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import java.util.Locale; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.i18n.LocaleContextHolder; + +@ExtendWith(MockitoExtension.class) +class RenameDirectoryDtoValidatorTest { + + @Mock + private StorageOperator storageOperator; + + @Mock + private TenantDao tenantDao; + + @InjectMocks + private RenameDirectoryDtoValidator renameDirectoryDtoValidator; + + private static final String BASE_DIRECTORY = "/tmp/dolphinscheduler"; + + private User loginUser; + + @BeforeEach + public void setup() { + when(storageOperator.getStorageBaseDirectory()).thenReturn(BASE_DIRECTORY); + loginUser = new User(); + loginUser.setTenantId(1); + LocaleContextHolder.setLocale(Locale.ENGLISH); + } + + @Test + void testValidate_notUnderBaseDirectory() { + RenameDirectoryDto renameDirectoryDto = RenameDirectoryDto.builder() + .loginUser(loginUser) + .originDirectoryAbsolutePath("/tmp") + .targetDirectoryAbsolutePath("/tmp1") + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp", + () -> renameDirectoryDtoValidator.validate(renameDirectoryDto)); + + } + + @Test + public void testValidate_directoryPathContainsIllegalSymbolic() { + RenameDirectoryDto renameDirectoryDto = RenameDirectoryDto.builder() + .loginUser(loginUser) + .originDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/..") + .targetDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/a") + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp/dolphinscheduler/default/resources/..", + () -> renameDirectoryDtoValidator.validate(renameDirectoryDto)); + } + + @Test + public void testValidate_originDirectoryNotExist() { + RenameDirectoryDto renameDirectoryDto = RenameDirectoryDto.builder() + .loginUser(loginUser) + .originDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/a") + .targetDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/b") + .build(); + assertThrowServiceException( + "Internal Server Error: Thr resource is not exists: /tmp/dolphinscheduler/default/resources/a", + () -> renameDirectoryDtoValidator.validate(renameDirectoryDto)); + } + + @Test + public void testValidate_originDirectoryNoPermission() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("test"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + RenameDirectoryDto renameDirectoryDto = RenameDirectoryDto.builder() + .loginUser(loginUser) + .originDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/a") + .targetDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/b") + .build(); + when(storageOperator.exists(renameDirectoryDto.getOriginDirectoryAbsolutePath())).thenReturn(true); + when(storageOperator.getResourceMetaData(renameDirectoryDto.getOriginDirectoryAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(renameDirectoryDto.getOriginDirectoryAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a") + .isDirectory(true) + .tenant("default") + .build()); + assertThrowServiceException( + "Internal Server Error: The user's tenant is test have no permission to access the resource: /tmp/dolphinscheduler/default/resources/a", + () -> renameDirectoryDtoValidator.validate(renameDirectoryDto)); + } + + @Test + public void testValidate_targetDirectoryAlreadyExist() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("default"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + RenameDirectoryDto renameDirectoryDto = RenameDirectoryDto.builder() + .loginUser(loginUser) + .originDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/a") + .targetDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/b") + .build(); + when(storageOperator.exists(renameDirectoryDto.getOriginDirectoryAbsolutePath())).thenReturn(true); + when(storageOperator.getResourceMetaData(renameDirectoryDto.getOriginDirectoryAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(renameDirectoryDto.getOriginDirectoryAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a") + .isDirectory(true) + .tenant("default") + .build()); + + when(storageOperator.exists(renameDirectoryDto.getTargetDirectoryAbsolutePath())).thenReturn(true); + assertThrowServiceException( + "Internal Server Error: The resource is already exist: /tmp/dolphinscheduler/default/resources/b", + () -> renameDirectoryDtoValidator.validate(renameDirectoryDto)); + } + + @Test + public void testValidate() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("default"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + RenameDirectoryDto renameDirectoryDto = RenameDirectoryDto.builder() + .loginUser(loginUser) + .originDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/a") + .targetDirectoryAbsolutePath("/tmp/dolphinscheduler/default/resources/b") + .build(); + when(storageOperator.exists(renameDirectoryDto.getOriginDirectoryAbsolutePath())).thenReturn(true); + when(storageOperator.getResourceMetaData(renameDirectoryDto.getOriginDirectoryAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(renameDirectoryDto.getOriginDirectoryAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a") + .isDirectory(true) + .tenant("default") + .build()); + + when(storageOperator.exists(renameDirectoryDto.getTargetDirectoryAbsolutePath())).thenReturn(false); + when(storageOperator.getResourceMetaData(renameDirectoryDto.getTargetDirectoryAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(renameDirectoryDto.getOriginDirectoryAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("b") + .isDirectory(true) + .tenant("default") + .build()); + + assertDoesNotThrow(() -> renameDirectoryDtoValidator.validate(renameDirectoryDto)); + } +} diff --git a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileDtoValidatorTest.java b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileDtoValidatorTest.java new file mode 100644 index 0000000000..79fcd8c124 --- /dev/null +++ b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/validator/resource/RenameFileDtoValidatorTest.java @@ -0,0 +1,202 @@ +/* + * 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.dolphinscheduler.api.validator.resource; + +import static org.apache.dolphinscheduler.api.AssertionsHelper.assertThrowServiceException; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.Mockito.when; + +import org.apache.dolphinscheduler.api.dto.resources.RenameFileDto; +import org.apache.dolphinscheduler.dao.entity.Tenant; +import org.apache.dolphinscheduler.dao.entity.User; +import org.apache.dolphinscheduler.dao.repository.TenantDao; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; + +import java.util.Locale; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.i18n.LocaleContextHolder; + +@ExtendWith(MockitoExtension.class) +public class RenameFileDtoValidatorTest { + + @Mock + private StorageOperator storageOperator; + + @Mock + private TenantDao tenantDao; + + @InjectMocks + private RenameFileDtoValidator renameFileDtoValidator; + + private static final String BASE_DIRECTORY = "/tmp/dolphinscheduler"; + + private User loginUser; + + @BeforeEach + public void setup() { + when(storageOperator.getStorageBaseDirectory()).thenReturn(BASE_DIRECTORY); + loginUser = new User(); + loginUser.setTenantId(1); + LocaleContextHolder.setLocale(Locale.ENGLISH); + } + + @Test + void testValidate_notUnderBaseDirectory() { + RenameFileDto renameFileDto = RenameFileDto.builder() + .loginUser(loginUser) + .originFileAbsolutePath("/tmp") + .targetFileAbsolutePath("/tmp1") + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp", + () -> renameFileDtoValidator.validate(renameFileDto)); + + } + + @Test + public void testValidate_fileAbsolutePathContainsIllegalSymbolic() { + RenameFileDto renameFileDto = RenameFileDto.builder() + .loginUser(loginUser) + .originFileAbsolutePath("/tmp/dolphinscheduler/default/resources/../a.txt") + .targetFileAbsolutePath("/tmp/dolphinscheduler/default/resources/b.txt") + .build(); + assertThrowServiceException( + "Internal Server Error: Invalidated resource path: /tmp/dolphinscheduler/default/resources/../a.txt", + () -> renameFileDtoValidator.validate(renameFileDto)); + } + + @Test + public void testValidate_originFileNotExist() { + RenameFileDto renameFileDto = RenameFileDto.builder() + .loginUser(loginUser) + .originFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.txt") + .targetFileAbsolutePath("/tmp/dolphinscheduler/default/resources/b.txt") + .build(); + assertThrowServiceException( + "Internal Server Error: Thr resource is not exists: /tmp/dolphinscheduler/default/resources/a.txt", + () -> renameFileDtoValidator.validate(renameFileDto)); + } + + @Test + public void testValidate_originFileIsNotFile() { + RenameFileDto renameFileDto = RenameFileDto.builder() + .loginUser(loginUser) + .originFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a") + .targetFileAbsolutePath("/tmp/dolphinscheduler/default/resources/b.txt") + .build(); + when(storageOperator.exists(renameFileDto.getOriginFileAbsolutePath())).thenReturn(true); + assertThrowServiceException( + "Internal Server Error: The path is not a file: /tmp/dolphinscheduler/default/resources/a", + () -> renameFileDtoValidator.validate(renameFileDto)); + } + + @Test + public void testValidate_originFileNoPermission() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("test"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + RenameFileDto renameFileDto = RenameFileDto.builder() + .loginUser(loginUser) + .originFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.txt") + .targetFileAbsolutePath("/tmp/dolphinscheduler/default/resources/b.txt") + .build(); + when(storageOperator.exists(renameFileDto.getOriginFileAbsolutePath())).thenReturn(true); + when(storageOperator.getResourceMetaData(renameFileDto.getOriginFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(renameFileDto.getOriginFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.txt") + .isDirectory(false) + .tenant("default") + .build()); + assertThrowServiceException( + "Internal Server Error: The user's tenant is test have no permission to access the resource: /tmp/dolphinscheduler/default/resources/a.txt", + () -> renameFileDtoValidator.validate(renameFileDto)); + } + + @Test + public void testValidate_targetFileAlreadyExist() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("default"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + RenameFileDto renameDirectoryDto = RenameFileDto.builder() + .loginUser(loginUser) + .originFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.txt") + .targetFileAbsolutePath("/tmp/dolphinscheduler/default/resources/b.txt") + .build(); + when(storageOperator.exists(renameDirectoryDto.getOriginFileAbsolutePath())).thenReturn(true); + when(storageOperator.getResourceMetaData(renameDirectoryDto.getOriginFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(renameDirectoryDto.getOriginFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.txt") + .isDirectory(false) + .tenant("default") + .build()); + + when(storageOperator.exists(renameDirectoryDto.getTargetFileAbsolutePath())).thenReturn(true); + assertThrowServiceException( + "Internal Server Error: The resource is already exist: /tmp/dolphinscheduler/default/resources/b.txt", + () -> renameFileDtoValidator.validate(renameDirectoryDto)); + } + + @Test + public void testValidate() { + Tenant tenant = new Tenant(); + tenant.setTenantCode("default"); + when(tenantDao.queryOptionalById(loginUser.getTenantId())).thenReturn(Optional.of(tenant)); + + RenameFileDto renameDirectoryDto = RenameFileDto.builder() + .loginUser(loginUser) + .originFileAbsolutePath("/tmp/dolphinscheduler/default/resources/a.txt") + .targetFileAbsolutePath("/tmp/dolphinscheduler/default/resources/b.txt") + .build(); + when(storageOperator.exists(renameDirectoryDto.getOriginFileAbsolutePath())).thenReturn(true); + when(storageOperator.getResourceMetaData(renameDirectoryDto.getOriginFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(renameDirectoryDto.getOriginFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("a.txt") + .isDirectory(false) + .tenant("default") + .build()); + + when(storageOperator.exists(renameDirectoryDto.getTargetFileAbsolutePath())).thenReturn(false); + when(storageOperator.getResourceMetaData(renameDirectoryDto.getTargetFileAbsolutePath())) + .thenReturn(ResourceMetadata.builder() + .resourceAbsolutePath(renameDirectoryDto.getTargetFileAbsolutePath()) + .resourceBaseDirectory(BASE_DIRECTORY) + .resourceRelativePath("b.txt") + .isDirectory(false) + .tenant("default") + .build()); + + assertDoesNotThrow(() -> renameFileDtoValidator.validate(renameDirectoryDto)); + } + +} diff --git a/dolphinscheduler-bom/pom.xml b/dolphinscheduler-bom/pom.xml index 10c4f0e4a1..8c59d1c4ca 100644 --- a/dolphinscheduler-bom/pom.xml +++ b/dolphinscheduler-bom/pom.xml @@ -965,6 +965,13 @@ test + + org.testcontainers + minio + ${testcontainer.version} + test + + org.checkerframework checker-qual diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/config/ImmutableYamlDelegate.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/config/ImmutableYamlDelegate.java index 5806a20fd7..6f4bb34eab 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/config/ImmutableYamlDelegate.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/config/ImmutableYamlDelegate.java @@ -43,6 +43,10 @@ public class ImmutableYamlDelegate implements IPropertyDelegate { // read from classpath for (String fileName : yamlAbsolutePath) { try (InputStream fis = ImmutableYamlDelegate.class.getResourceAsStream(fileName)) { + if (fis == null) { + log.warn("Cannot find the file: {} under classpath", fileName); + continue; + } YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new InputStreamResource(fis)); factory.afterPropertiesSet(); diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/Constants.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/Constants.java index 5ec2a2b181..4800bff7f9 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/Constants.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/Constants.java @@ -59,28 +59,6 @@ public final class Constants { */ public static final String HDFS_DEFAULT_FS = "fs.defaultFS"; - /** - * hadoop configuration - */ - public static final String HADOOP_RM_STATE_ACTIVE = "ACTIVE"; - - public static final String HADOOP_RESOURCE_MANAGER_HTTPADDRESS_PORT = "resource.manager.httpaddress.port"; - - /** - * yarn.resourcemanager.ha.rm.ids - */ - public static final String YARN_RESOURCEMANAGER_HA_RM_IDS = "yarn.resourcemanager.ha.rm.ids"; - - /** - * yarn.application.status.address - */ - public static final String YARN_APPLICATION_STATUS_ADDRESS = "yarn.application.status.address"; - - /** - * yarn.job.history.status.address - */ - public static final String YARN_JOB_HISTORY_STATUS_ADDRESS = "yarn.job.history.status.address"; - /** * hdfs configuration * resource.hdfs.root.user @@ -294,8 +272,6 @@ public final class Constants { */ public static final String FLOWNODE_RUN_FLAG_NORMAL = "NORMAL"; - public static final String COMMON_TASK_TYPE = "common"; - public static final String DEFAULT = "default"; public static final String PASSWORD = "password"; public static final String XXXXXX = "******"; @@ -392,8 +368,6 @@ public final class Constants { public static final String QUEUE_NAME = "queueName"; public static final int LOG_QUERY_SKIP_LINE_NUMBER = 0; public static final int LOG_QUERY_LIMIT = 4096; - public static final String ALIAS = "alias"; - public static final String CONTENT = "content"; public static final String DEPENDENT_SPLIT = ":||"; public static final long DEPENDENT_ALL_TASK_CODE = -1; public static final long DEPENDENT_WORKFLOW_CODE = 0; @@ -532,14 +506,9 @@ public final class Constants { * session timeout */ public static final int SESSION_TIME_OUT = 7200; - public static final int MAX_FILE_SIZE = 1024 * 1024 * 1024; public static final String UDF = "UDF"; public static final String CLASS = "class"; - /** - * default worker group - */ - public static final String DEFAULT_WORKER_GROUP = "default"; /** * authorize writable perm */ diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/StateEventType.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/StateEventType.java index 6e25d2ed4b..220b56a939 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/StateEventType.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/StateEventType.java @@ -27,7 +27,6 @@ public enum StateEventType { TASK_TIMEOUT(3, "task timeout"), WAKE_UP_TASK_GROUP(4, "wait task group"), TASK_RETRY(5, "task retry"), - PROCESS_BLOCKED(6, "process blocked"), PROCESS_SUBMIT_FAILED(7, "process submit failed"); StateEventType(int code, String descp) { diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/ResUploadType.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/StorageType.java similarity index 97% rename from dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/ResUploadType.java rename to dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/StorageType.java index d60a657002..ba3cad45cd 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/ResUploadType.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/StorageType.java @@ -20,6 +20,6 @@ package org.apache.dolphinscheduler.common.enums; /** * data base types */ -public enum ResUploadType { +public enum StorageType { LOCAL, HDFS, S3, OSS, GCS, ABS, OBS } diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/WorkflowExecutionStatus.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/WorkflowExecutionStatus.java index 880db60257..923192ea7b 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/WorkflowExecutionStatus.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/enums/WorkflowExecutionStatus.java @@ -38,8 +38,6 @@ public enum WorkflowExecutionStatus { SUCCESS(7, "success"), DELAY_EXECUTION(12, "delay execution"), SERIAL_WAIT(14, "serial wait"), - READY_BLOCK(15, "ready block"), - BLOCK(16, "block"), WAIT_TO_RUN(17, "wait to run"), ; @@ -59,7 +57,6 @@ public enum WorkflowExecutionStatus { READY_PAUSE.getCode(), READY_STOP.getCode(), SERIAL_WAIT.getCode(), - READY_BLOCK.getCode(), WAIT_TO_RUN.getCode() }; @@ -91,7 +88,7 @@ public enum WorkflowExecutionStatus { public boolean isFinished() { // todo: do we need to remove pause/block in finished judge? - return isSuccess() || isFailure() || isStop() || isPause() || isBlock(); + return isSuccess() || isFailure() || isStop() || isPause(); } /** @@ -119,10 +116,6 @@ public enum WorkflowExecutionStatus { return this == STOP; } - public boolean isBlock() { - return this == BLOCK; - } - public static int[] getNeedFailoverWorkflowInstanceState() { return NEED_FAILOVER_STATES; } diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/FileUtils.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/FileUtils.java index 60629576d9..31765b19bd 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/FileUtils.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/FileUtils.java @@ -17,14 +17,15 @@ package org.apache.dolphinscheduler.common.utils; +import static com.google.common.base.Preconditions.checkNotNull; import static org.apache.dolphinscheduler.common.constants.Constants.DATA_BASEDIR_PATH; import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_VIEW_SUFFIXES; import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_VIEW_SUFFIXES_DEFAULT_VALUE; -import static org.apache.dolphinscheduler.common.constants.DateConstants.YYYYMMDDHHMMSS; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import java.io.ByteArrayOutputStream; @@ -33,17 +34,21 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; +import java.util.Optional; import java.util.Set; import java.util.zip.CRC32; import java.util.zip.CheckedInputStream; import lombok.NonNull; +import lombok.SneakyThrows; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; @@ -66,32 +71,14 @@ public class FileUtils { * @return download file name */ public static String getDownloadFilename(String filename) { - String fileName = - String.format("%s/download/%s/%s", DATA_BASEDIR, DateUtils.getCurrentTime(YYYYMMDDHHMMSS), filename); - - File file = new File(fileName); - if (!file.getParentFile().exists()) { - file.getParentFile().mkdirs(); - } - - return fileName; + return Paths.get(DATA_BASEDIR, "tmp", CodeGenerateUtils.genCode() + "-" + filename).toString(); } /** - * get upload file absolute path and name - * - * @param tenantCode tenant code - * @param filename file name - * @return local file path + * Generate a local tmp absolute path of the uploaded file */ - public static String getUploadFilename(String tenantCode, String filename) { - String fileName = String.format("%s/%s/resources/%s", DATA_BASEDIR, tenantCode, filename); - File file = new File(fileName); - if (!file.getParentFile().exists()) { - file.getParentFile().mkdirs(); - } - - return fileName; + public static String getUploadFileLocalTmpAbsolutePath() { + return Paths.get(DATA_BASEDIR, "tmp", String.valueOf(CodeGenerateUtils.genCode())).toString(); } /** @@ -135,7 +122,7 @@ public class FileUtils { /** * absolute path of appInfo file * - * @param execPath directory of process execution + * @param execPath directory of process execution * @return */ public static String getAppInfoPath(String execPath) { @@ -152,7 +139,7 @@ public class FileUtils { /** * write content to file ,if parent path not exists, it will do one's utmost to mkdir * - * @param content content + * @param content content * @param filePath target file path * @return true if write success */ @@ -236,6 +223,7 @@ public class FileUtils { /** * Calculate file checksum with CRC32 algorithm + * * @param pathName * @return checksum of file/dir */ @@ -315,4 +303,45 @@ public class FileUtils { } } + public static String concatFilePath(String... paths) { + if (paths.length == 0) { + throw new IllegalArgumentException("At least one path should be provided"); + } + StringBuilder finalPath = new StringBuilder(paths[0]); + if (StringUtils.isEmpty(finalPath)) { + throw new IllegalArgumentException("The path should not be empty"); + } + String separator = File.separator; + for (int i = 1; i < paths.length; i++) { + String path = paths[i]; + if (StringUtils.isEmpty(path)) { + throw new IllegalArgumentException("The path should not be empty"); + } + if (finalPath.toString().endsWith(separator) && path.startsWith(separator)) { + finalPath.append(path.substring(separator.length())); + continue; + } + if (!finalPath.toString().endsWith(separator) && !path.startsWith(separator)) { + finalPath.append(separator).append(path); + continue; + } + finalPath.append(path); + } + return finalPath.toString(); + } + + public static String getClassPathAbsolutePath(Class clazz) { + checkNotNull(clazz, "class is null"); + return Optional.ofNullable(clazz.getResource("/")) + .map(URL::getPath) + .orElseThrow(() -> new IllegalArgumentException("class path: " + clazz + " is null")); + } + + /** + * copy input stream to file, if the file already exists, will append the content to the beginning of the file, otherwise will create a new file. + */ + @SneakyThrows + public static void copyInputStreamToFile(InputStream inputStream, String destFilename) { + org.apache.commons.io.FileUtils.copyInputStreamToFile(inputStream, new File(destFilename)); + } } diff --git a/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/FileUtilsTest.java b/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/FileUtilsTest.java index f06ece2a56..72ceed630f 100644 --- a/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/FileUtilsTest.java +++ b/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/FileUtilsTest.java @@ -17,8 +17,6 @@ package org.apache.dolphinscheduler.common.utils; -import static org.apache.dolphinscheduler.common.constants.DateConstants.YYYYMMDDHHMMSS; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -31,26 +29,21 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.MockedStatic; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import com.google.common.truth.Truth; + @ExtendWith(MockitoExtension.class) public class FileUtilsTest { @Test public void testGetDownloadFilename() { - try (MockedStatic mockedDateUtils = Mockito.mockStatic(DateUtils.class)) { - mockedDateUtils.when(() -> DateUtils.getCurrentTime(YYYYMMDDHHMMSS)).thenReturn("20190101101059"); - Assertions.assertEquals("/tmp/dolphinscheduler/download/20190101101059/test", - FileUtils.getDownloadFilename("test")); - } + Truth.assertThat(FileUtils.getDownloadFilename("test")).startsWith("/tmp/dolphinscheduler/tmp/"); } @Test public void testGetUploadFilename() { - Assertions.assertEquals("/tmp/dolphinscheduler/aaa/resources/bbb", - FileUtils.getUploadFilename("aaa", "bbb")); + Truth.assertThat(FileUtils.getUploadFileLocalTmpAbsolutePath()).startsWith("/tmp/dolphinscheduler/tmp/"); } @Test diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/DependentProcessDefinition.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/DependentProcessDefinition.java index 9fd77c944f..fcce2a33d3 100644 --- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/DependentProcessDefinition.java +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/DependentProcessDefinition.java @@ -17,7 +17,6 @@ package org.apache.dolphinscheduler.dao.entity; -import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.CycleEnum; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.model.DependentItem; @@ -67,7 +66,7 @@ public class DependentProcessDefinition { */ public CycleEnum getDependentCycle(long upstreamProcessDefinitionCode) { DependentParameters dependentParameters = this.getDependentParameters(); - List dependentTaskModelList = dependentParameters.getDependTaskList(); + List dependentTaskModelList = dependentParameters.getDependence().getDependTaskList(); for (DependentTaskModel dependentTaskModel : dependentTaskModelList) { List dependentItemList = dependentTaskModel.getDependItemList(); @@ -104,11 +103,7 @@ public class DependentProcessDefinition { } public DependentParameters getDependentParameters() { - return JSONUtils.parseObject(getDependence(), DependentParameters.class); - } - - public String getDependence() { - return JSONUtils.getNodeString(this.taskParams, Constants.DEPENDENCE); + return JSONUtils.parseObject(taskParams, DependentParameters.class); } public String getProcessDefinitionName() { diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/ProcessInstance.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/ProcessInstance.java index d8f01f473f..9ed8bca0ef 100644 --- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/ProcessInstance.java +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/ProcessInstance.java @@ -201,12 +201,6 @@ public class ProcessInstance { */ private Date restartTime; - /** - * workflow block flag - */ - @TableField(exist = false) - private boolean isBlocked; - /** * test flag */ diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/TaskInstance.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/TaskInstance.java index 4897449753..629cfe9aea 100644 --- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/TaskInstance.java +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/TaskInstance.java @@ -17,26 +17,15 @@ package org.apache.dolphinscheduler.dao.entity; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_BLOCKING; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_CONDITIONS; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_DEPENDENT; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_DYNAMIC; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SUB_PROCESS; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SWITCH; - -import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.Flag; import org.apache.dolphinscheduler.common.enums.Priority; import org.apache.dolphinscheduler.common.enums.TaskExecuteType; import org.apache.dolphinscheduler.common.utils.DateUtils; -import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; -import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.SwitchParameters; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import java.io.Serializable; import java.util.Date; -import java.util.Map; import java.util.concurrent.TimeUnit; import lombok.Data; @@ -46,7 +35,6 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.fasterxml.jackson.core.type.TypeReference; /** * task instance @@ -186,18 +174,6 @@ public class TaskInstance implements Serializable { @TableField(updateStrategy = FieldStrategy.IGNORED) private String cacheKey; - /** - * dependency - */ - @TableField(exist = false) - private DependentParameters dependency; - - /** - * switch dependency - */ - @TableField(exist = false) - private SwitchParameters switchDependency; - /** * duration */ @@ -303,43 +279,6 @@ public class TaskInstance implements Serializable { this.executePath = executePath; } - public DependentParameters getDependency() { - if (this.dependency == null) { - Map taskParamsMap = - JSONUtils.parseObject(this.getTaskParams(), new TypeReference>() { - }); - this.dependency = - JSONUtils.parseObject((String) taskParamsMap.get(Constants.DEPENDENCE), DependentParameters.class); - } - return this.dependency; - } - - public void setDependency(DependentParameters dependency) { - this.dependency = dependency; - } - - public SwitchParameters getSwitchDependency() { - // todo: We need to directly use Jackson to deserialize the taskParam, rather than parse the map and get from - // field. - if (this.switchDependency == null) { - Map taskParamsMap = - JSONUtils.parseObject(this.getTaskParams(), new TypeReference>() { - }); - this.switchDependency = - JSONUtils.parseObject((String) taskParamsMap.get(Constants.SWITCH_RESULT), SwitchParameters.class); - } - return this.switchDependency; - } - - public void setSwitchDependency(SwitchParameters switchDependency) { - Map taskParamsMap = - JSONUtils.parseObject(this.getTaskParams(), new TypeReference>() { - }); - taskParamsMap.put(Constants.SWITCH_RESULT, JSONUtils.toJsonString(switchDependency)); - this.switchDependency = switchDependency; - this.setTaskParams(JSONUtils.toJsonString(taskParamsMap)); - } - public boolean isTaskComplete() { return this.getState().isSuccess() @@ -348,30 +287,6 @@ public class TaskInstance implements Serializable { || this.getState().isForceSuccess(); } - public boolean isSubProcess() { - return TASK_TYPE_SUB_PROCESS.equalsIgnoreCase(this.taskType); - } - - public boolean isDependTask() { - return TASK_TYPE_DEPENDENT.equalsIgnoreCase(this.taskType); - } - - public boolean isDynamic() { - return TASK_TYPE_DYNAMIC.equalsIgnoreCase(this.taskType); - } - - public boolean isConditionsTask() { - return TASK_TYPE_CONDITIONS.equalsIgnoreCase(this.taskType); - } - - public boolean isSwitchTask() { - return TASK_TYPE_SWITCH.equalsIgnoreCase(this.taskType); - } - - public boolean isBlockingTask() { - return TASK_TYPE_BLOCKING.equalsIgnoreCase(this.taskType); - } - public boolean isFirstRun() { return endTime == null; } @@ -383,7 +298,7 @@ public class TaskInstance implements Serializable { * @return can try result */ public boolean taskCanRetry() { - if (this.isSubProcess()) { + if (TaskTypeUtils.isSubWorkflowTask(getTaskType())) { return false; } if (this.getState() == TaskExecutionStatus.NEED_FAULT_TOLERANCE) { diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/TenantDao.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/TenantDao.java new file mode 100644 index 0000000000..9f48f372da --- /dev/null +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/TenantDao.java @@ -0,0 +1,24 @@ +/* + * 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.dolphinscheduler.dao.repository; + +import org.apache.dolphinscheduler.dao.entity.Tenant; + +public interface TenantDao extends IDao { + +} diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/impl/TenantDaoImpl.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/impl/TenantDaoImpl.java new file mode 100644 index 0000000000..45f9a5fcba --- /dev/null +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/repository/impl/TenantDaoImpl.java @@ -0,0 +1,36 @@ +/* + * 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.dolphinscheduler.dao.repository.impl; + +import org.apache.dolphinscheduler.dao.entity.Tenant; +import org.apache.dolphinscheduler.dao.mapper.TenantMapper; +import org.apache.dolphinscheduler.dao.repository.BaseDao; +import org.apache.dolphinscheduler.dao.repository.TenantDao; + +import lombok.NonNull; + +import org.springframework.stereotype.Repository; + +@Repository +public class TenantDaoImpl extends BaseDao implements TenantDao { + + public TenantDaoImpl(@NonNull TenantMapper tenantMapper) { + super(tenantMapper); + } + +} diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/EnvironmentUtils.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/EnvironmentUtils.java new file mode 100644 index 0000000000..89ea647f75 --- /dev/null +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/EnvironmentUtils.java @@ -0,0 +1,54 @@ +/* + * 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.dolphinscheduler.dao.utils; + +public class EnvironmentUtils { + + private static final long EMPTY_ENVIRONMENT_CODE = -1L; + + /** + * Check if the environment code is empty (we should use null instead of -1, this is used to comply with the original code) + * + * @return true if the environment code is empty, false otherwise + */ + public static boolean isEnvironmentCodeEmpty(Long environmentCode) { + return environmentCode == null || environmentCode <= 0; + } + + /** + * Get the empty environment code + */ + public static Long getDefaultEnvironmentCode() { + return EMPTY_ENVIRONMENT_CODE; + } + + /** + * Get the environment code or the default environment code if the environment code is empty + */ + public static Long getEnvironmentCodeOrDefault(Long environmentCode) { + return getEnvironmentCodeOrDefault(environmentCode, getDefaultEnvironmentCode()); + } + + /** + * Get the environment code or the default environment code if the environment code is empty + */ + public static Long getEnvironmentCodeOrDefault(Long environmentCode, Long defaultEnvironmentCode) { + return isEnvironmentCodeEmpty(environmentCode) ? defaultEnvironmentCode : environmentCode; + } + +} diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/TaskCacheUtils.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/TaskCacheUtils.java index 36cf6c2356..90b648386b 100644 --- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/TaskCacheUtils.java +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/TaskCacheUtils.java @@ -22,7 +22,7 @@ import static org.apache.dolphinscheduler.common.constants.Constants.CRC_SUFFIX; import org.apache.dolphinscheduler.common.utils.FileUtils; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.enums.DataType; import org.apache.dolphinscheduler.plugin.task.api.enums.Direct; @@ -65,17 +65,17 @@ public class TaskCacheUtils { * 4. input VarPool, from upstream task and workflow global parameters * @param taskInstance task instance * @param taskExecutionContext taskExecutionContext - * @param storageOperate storageOperate + * @param storageOperator storageOperate * @return cache key */ public static String generateCacheKey(TaskInstance taskInstance, TaskExecutionContext taskExecutionContext, - StorageOperate storageOperate) { + StorageOperator storageOperator) { List keyElements = new ArrayList<>(); keyElements.add(String.valueOf(taskInstance.getTaskCode())); keyElements.add(String.valueOf(taskInstance.getTaskDefinitionVersion())); keyElements.add(String.valueOf(taskInstance.getIsCache().getCode())); keyElements.add(String.valueOf(taskInstance.getEnvironmentConfig())); - keyElements.add(getTaskInputVarPoolData(taskInstance, taskExecutionContext, storageOperate)); + keyElements.add(getTaskInputVarPoolData(taskInstance, taskExecutionContext, storageOperator)); String data = StringUtils.join(keyElements, "_"); return DigestUtils.sha256Hex(data); } @@ -123,7 +123,7 @@ public class TaskCacheUtils { * taskExecutionContext taskExecutionContext */ public static String getTaskInputVarPoolData(TaskInstance taskInstance, TaskExecutionContext context, - StorageOperate storageOperate) { + StorageOperator storageOperator) { JsonNode taskParams = JSONUtils.parseObject(taskInstance.getTaskParams()); // The set of input values considered from localParams in the taskParams @@ -141,7 +141,8 @@ public class TaskCacheUtils { List fileInput = varPool.stream().filter(property -> property.getType().equals(DataType.FILE)) .collect(Collectors.toList()); fileInput.forEach( - property -> fileCheckSumMap.put(property.getProp(), getValCheckSum(property, context, storageOperate))); + property -> fileCheckSumMap.put(property.getProp(), + getValCheckSum(property, context, storageOperator))); // var pool value from workflow global parameters if (context.getPrepareParamsMap() != null) { @@ -173,17 +174,18 @@ public class TaskCacheUtils { * cache can be used if content of upstream output files are the same * @param fileProperty * @param context - * @param storageOperate + * @param storageOperator */ public static String getValCheckSum(Property fileProperty, TaskExecutionContext context, - StorageOperate storageOperate) { + StorageOperator storageOperator) { String resourceCRCPath = fileProperty.getValue() + CRC_SUFFIX; - String resourceCRCWholePath = storageOperate.getResourceFullName(context.getTenantCode(), resourceCRCPath); + String resourceCRCWholePath = + storageOperator.getStorageFileAbsolutePath(context.getTenantCode(), resourceCRCPath); String targetPath = String.format("%s/%s", context.getExecutePath(), resourceCRCPath); log.info("{} --- Remote:{} to Local:{}", "CRC file", resourceCRCWholePath, targetPath); String crcString = ""; try { - storageOperate.download(resourceCRCWholePath, targetPath, true); + storageOperator.download(resourceCRCWholePath, targetPath, true); crcString = FileUtils.readFile2Str(new FileInputStream(targetPath)); fileProperty.setValue(crcString); } catch (IOException e) { diff --git a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/TaskInstanceUtils.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/TaskInstanceUtils.java index f75a2f5e11..e23fcb97b5 100644 --- a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/TaskInstanceUtils.java +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/TaskInstanceUtils.java @@ -55,11 +55,9 @@ public class TaskInstanceUtils { target.setPid(source.getPid()); target.setAppLink(source.getAppLink()); target.setFlag(source.getFlag()); - target.setDependency(source.getDependency()); // todo: we need to cpoy the task params and then copy switchDependency, since the setSwitchDependency rely on // task params, this is really a very bad practice. target.setTaskParams(source.getTaskParams()); - target.setSwitchDependency(source.getSwitchDependency()); target.setDuration(source.getDuration()); target.setMaxRetryTimes(source.getMaxRetryTimes()); target.setRetryInterval(source.getRetryInterval()); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/BlockingParameters.java b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/WorkerGroupUtils.java similarity index 51% rename from dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/BlockingParameters.java rename to dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/WorkerGroupUtils.java index 2942a08c30..2436d45a02 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/BlockingParameters.java +++ b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/utils/WorkerGroupUtils.java @@ -15,37 +15,31 @@ * limitations under the License. */ -package org.apache.dolphinscheduler.plugin.task.api.parameters; +package org.apache.dolphinscheduler.dao.utils; import org.apache.commons.lang3.StringUtils; -public class BlockingParameters extends AbstractParameters { +public class WorkerGroupUtils { - // condition of blocking: BlockingOnFailed or BlockingOnSuccess - private String blockingOpportunity; + private static final String DEFAULT_WORKER_GROUP = "default"; - // if true, alert when blocking, otherwise do nothing - - private boolean isAlertWhenBlocking; - - @Override - public boolean checkParameters() { - return !StringUtils.isEmpty(blockingOpportunity); + /** + * Check if the worker group is empty, if the worker group is default, it is considered empty + */ + public static boolean isWorkerGroupEmpty(String workerGroup) { + return StringUtils.isEmpty(workerGroup) || getDefaultWorkerGroup().equals(workerGroup); } - public String getBlockingOpportunity() { - return blockingOpportunity; + public static String getWorkerGroupOrDefault(String workerGroup) { + return getWorkerGroupOrDefault(workerGroup, getDefaultWorkerGroup()); } - public void setBlockingCondition(String blockingOpportunity) { - this.blockingOpportunity = blockingOpportunity; + public static String getWorkerGroupOrDefault(String workerGroup, String defaultWorkerGroup) { + return isWorkerGroupEmpty(workerGroup) ? defaultWorkerGroup : workerGroup; } - public boolean isAlertWhenBlocking() { - return isAlertWhenBlocking; + public static String getDefaultWorkerGroup() { + return DEFAULT_WORKER_GROUP; } - public void setAlertWhenBlocking(boolean alertWhenBlocking) { - isAlertWhenBlocking = alertWhenBlocking; - } } diff --git a/dolphinscheduler-dao/src/main/resources/sql/upgrade/3.2.0_schema/mysql/dolphinscheduler_dml.sql b/dolphinscheduler-dao/src/main/resources/sql/upgrade/3.2.0_schema/mysql/dolphinscheduler_dml.sql index e5d97fab94..21189c77a3 100644 --- a/dolphinscheduler-dao/src/main/resources/sql/upgrade/3.2.0_schema/mysql/dolphinscheduler_dml.sql +++ b/dolphinscheduler-dao/src/main/resources/sql/upgrade/3.2.0_schema/mysql/dolphinscheduler_dml.sql @@ -30,6 +30,9 @@ END IF; END; d// +-- If the admin account is not associated with a tenant, the admin's tenant will be set to the default tenant. +UPDATE `t_ds_user` SET `tenant_id` = '-1' WHERE (`user_name` = 'admin') AND (`tenant_id` = '0'); + delimiter ; CALL dolphin_t_ds_tenant_insert_default(); DROP PROCEDURE dolphin_t_ds_tenant_insert_default; diff --git a/dolphinscheduler-dao/src/main/resources/sql/upgrade/3.2.0_schema/postgresql/dolphinscheduler_dml.sql b/dolphinscheduler-dao/src/main/resources/sql/upgrade/3.2.0_schema/postgresql/dolphinscheduler_dml.sql index 482b317a60..f47d16f7c7 100644 --- a/dolphinscheduler-dao/src/main/resources/sql/upgrade/3.2.0_schema/postgresql/dolphinscheduler_dml.sql +++ b/dolphinscheduler-dao/src/main/resources/sql/upgrade/3.2.0_schema/postgresql/dolphinscheduler_dml.sql @@ -21,6 +21,9 @@ INSERT INTO t_ds_tenant(id, tenant_code, description, queue_id, create_time, upd UPDATE t_ds_schedules as t1 SET tenant_code = COALESCE(t3.tenant_code, 'default') FROM t_ds_process_definition as t2 LEFT JOIN t_ds_tenant t3 ON t2.tenant_id = t3.id WHERE t1.process_definition_code = t2.code; UPDATE t_ds_process_instance SET tenant_code = 'default' WHERE tenant_code IS NULL; +-- If the admin account is not associated with a tenant, the admin's tenant will be set to the default tenant. +UPDATE t_ds_user SET tenant_id = '-1' WHERE (user_name = 'admin') AND (tenant_id = '0'); + -- data quality support choose database INSERT INTO t_ds_dq_rule_input_entry (id, field, "type", title, value, "options", placeholder, option_source_type, value_type, input_type, is_show, can_edit, is_emit, is_validate, create_time, update_time) diff --git a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/entity/TaskInstanceTest.java b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/entity/TaskInstanceTest.java index cb68cf7494..1bcc784cbc 100644 --- a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/entity/TaskInstanceTest.java +++ b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/entity/TaskInstanceTest.java @@ -17,12 +17,7 @@ package org.apache.dolphinscheduler.dao.entity; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_CONDITIONS; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_DEPENDENT; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SUB_PROCESS; - import org.apache.dolphinscheduler.common.utils.JSONUtils; -import org.apache.dolphinscheduler.plugin.task.api.enums.DependentRelation; import org.apache.dolphinscheduler.plugin.task.api.model.DependentItem; import org.apache.dolphinscheduler.plugin.task.api.model.DependentTaskModel; import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameters; @@ -30,35 +25,10 @@ import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameter import java.util.ArrayList; import java.util.List; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TaskInstanceTest { - /** - * task instance sub process - */ - @Test - public void testTaskInstanceIsSubProcess() { - TaskInstance taskInstance = new TaskInstance(); - - // sub process - taskInstance.setTaskType(TASK_TYPE_SUB_PROCESS); - Assertions.assertTrue(taskInstance.isSubProcess()); - - // not sub process - taskInstance.setTaskType("HTTP"); - Assertions.assertFalse(taskInstance.isSubProcess()); - - // sub process - taskInstance.setTaskType(TASK_TYPE_CONDITIONS); - Assertions.assertTrue(taskInstance.isConditionsTask()); - - // sub process - taskInstance.setTaskType(TASK_TYPE_DEPENDENT); - Assertions.assertTrue(taskInstance.isDependTask()); - } - /** * test for TaskInstance.getDependence */ @@ -66,7 +36,6 @@ public class TaskInstanceTest { public void testTaskInstanceGetDependence() { TaskInstance taskInstance = new TaskInstance(); taskInstance.setTaskParams(JSONUtils.toJsonString(getDependentParameters())); - taskInstance.getDependency(); } /** @@ -82,8 +51,6 @@ public class TaskInstanceTest { dependentItem.setDefinitionCode(222L); dependentItem.setCycle("today"); dependentItems.add(dependentItem); - dependentParameters.setDependTaskList(dependTaskList); - dependentParameters.setRelation(DependentRelation.AND); return dependentParameters; } } diff --git a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/CommandMapperTest.java b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/CommandMapperTest.java index 2105885fa3..3f4b0768aa 100644 --- a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/CommandMapperTest.java +++ b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/CommandMapperTest.java @@ -19,7 +19,6 @@ package org.apache.dolphinscheduler.dao.mapper; import static com.google.common.truth.Truth.assertThat; -import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.CommandType; import org.apache.dolphinscheduler.common.enums.FailureStrategy; import org.apache.dolphinscheduler.common.enums.Flag; @@ -32,6 +31,7 @@ import org.apache.dolphinscheduler.dao.BaseDaoTest; import org.apache.dolphinscheduler.dao.entity.Command; import org.apache.dolphinscheduler.dao.entity.CommandCount; import org.apache.dolphinscheduler.dao.entity.ProcessDefinition; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import java.util.Date; import java.util.HashMap; @@ -303,7 +303,7 @@ public class CommandMapperTest extends BaseDaoTest { command.setProcessInstancePriority(Priority.MEDIUM); command.setStartTime(DateUtils.stringToDate("2019-12-29 10:10:00")); command.setUpdateTime(DateUtils.stringToDate("2019-12-29 10:10:00")); - command.setWorkerGroup(Constants.DEFAULT_WORKER_GROUP); + command.setWorkerGroup(WorkerGroupUtils.getDefaultWorkerGroup()); command.setProcessInstanceId(0); command.setProcessDefinitionVersion(0); commandMapper.insert(command); diff --git a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/repository/impl/CommandDaoImplTest.java b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/repository/impl/CommandDaoImplTest.java index 85867ef3b5..df5a2562aa 100644 --- a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/repository/impl/CommandDaoImplTest.java +++ b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/repository/impl/CommandDaoImplTest.java @@ -18,8 +18,8 @@ package org.apache.dolphinscheduler.dao.repository.impl; import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; -import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.CommandType; import org.apache.dolphinscheduler.common.enums.FailureStrategy; import org.apache.dolphinscheduler.common.enums.Priority; @@ -29,44 +29,58 @@ import org.apache.dolphinscheduler.common.utils.DateUtils; import org.apache.dolphinscheduler.dao.BaseDaoTest; import org.apache.dolphinscheduler.dao.entity.Command; import org.apache.dolphinscheduler.dao.repository.CommandDao; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.commons.lang3.RandomUtils; import java.util.List; +import java.util.stream.Collectors; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.RepeatedTest; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) class CommandDaoImplTest extends BaseDaoTest { @Autowired private CommandDao commandDao; - @Test + @RepeatedTest(value = 10) void fetchCommandByIdSlot() { - int commandSize = RandomUtils.nextInt(1, 1000); - for (int i = 0; i < commandSize; i++) { - createCommand(CommandType.START_PROCESS, 0); - } int totalSlot = RandomUtils.nextInt(1, 10); int currentSlotIndex = RandomUtils.nextInt(0, totalSlot); int fetchSize = RandomUtils.nextInt(10, 100); - for (int i = 1; i < 5; i++) { - int idStep = i; - List commands = commandDao.queryCommandByIdSlot(currentSlotIndex, totalSlot, idStep, fetchSize); - assertThat(commands.size()).isGreaterThan(0); - assertThat(commands.size()) - .isEqualTo(commandDao.queryAll() - .stream() - .filter(command -> (command.getId() / idStep) % totalSlot == currentSlotIndex) - .limit(fetchSize) - .count()); - + int idStep = RandomUtils.nextInt(1, 5); + int commandSize = RandomUtils.nextInt(currentSlotIndex, 1000); + // Generate commandSize commands + int id = 1; + for (int j = 0; j < commandSize; j++) { + Command command = generateCommand(CommandType.START_PROCESS, 0); + command.setId(id); + commandDao.insert(command); + id += idStep; } + List commands = commandDao.queryCommandByIdSlot(currentSlotIndex, totalSlot, idStep, fetchSize); + assertFalse(commands.isEmpty(), + "Commands should not be empty, currentSlotIndex: " + currentSlotIndex + + ", totalSlot: " + totalSlot + + ", idStep: " + idStep + + ", fetchSize: " + fetchSize + + ", total command size: " + commandSize + + ", total commands: " + + commandDao.queryAll().stream().map(Command::getId).collect(Collectors.toList())); + assertThat(commands.size()) + .isEqualTo(commandDao.queryAll() + .stream() + .filter(command -> (command.getId() / idStep) % totalSlot == currentSlotIndex) + .limit(fetchSize) + .count()); + } - private void createCommand(CommandType commandType, int processDefinitionCode) { + private Command generateCommand(CommandType commandType, int processDefinitionCode) { Command command = new Command(); command.setCommandType(commandType); command.setProcessDefinitionCode(processDefinitionCode); @@ -80,9 +94,9 @@ class CommandDaoImplTest extends BaseDaoTest { command.setProcessInstancePriority(Priority.MEDIUM); command.setStartTime(DateUtils.stringToDate("2019-12-29 10:10:00")); command.setUpdateTime(DateUtils.stringToDate("2019-12-29 10:10:00")); - command.setWorkerGroup(Constants.DEFAULT_WORKER_GROUP); + command.setWorkerGroup(WorkerGroupUtils.getDefaultWorkerGroup()); command.setProcessInstanceId(0); command.setProcessDefinitionVersion(0); - commandDao.insert(command); + return command; } } diff --git a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/repository/impl/ProcessInstanceDaoImplTest.java b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/repository/impl/ProcessInstanceDaoImplTest.java index 421309130f..9f69b7d90f 100644 --- a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/repository/impl/ProcessInstanceDaoImplTest.java +++ b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/repository/impl/ProcessInstanceDaoImplTest.java @@ -65,11 +65,9 @@ class ProcessInstanceDaoImplTest extends BaseDaoTest { WorkflowExecutionStatus.READY_STOP)); processInstanceDao.insert(createWorkflowInstance(workflowDefinitionCode, workflowDefinitionVersion, WorkflowExecutionStatus.SERIAL_WAIT)); - processInstanceDao.insert(createWorkflowInstance(workflowDefinitionCode, workflowDefinitionVersion, - WorkflowExecutionStatus.READY_BLOCK)); processInstanceDao.insert(createWorkflowInstance(workflowDefinitionCode, workflowDefinitionVersion, WorkflowExecutionStatus.WAIT_TO_RUN)); - assertEquals(8, processInstanceDao + assertEquals(7, processInstanceDao .queryByWorkflowCodeVersionStatus(workflowDefinitionCode, workflowDefinitionVersion, status).size()); } diff --git a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/EnvironmentUtilsTest.java b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/EnvironmentUtilsTest.java new file mode 100644 index 0000000000..dd5356169d --- /dev/null +++ b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/EnvironmentUtilsTest.java @@ -0,0 +1,72 @@ +/* + * 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.dolphinscheduler.dao.utils; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +class EnvironmentUtilsTest { + + @ParameterizedTest + @ValueSource(longs = {0, -1}) + void testIsEnvironmentCodeEmpty_emptyEnvironmentCode(Long environmentCode) { + assertThat(EnvironmentUtils.isEnvironmentCodeEmpty(environmentCode)).isTrue(); + } + + @ParameterizedTest + @ValueSource(longs = {123}) + void testIsEnvironmentCodeEmpty_nonEmptyEnvironmentCode(Long environmentCode) { + assertThat(EnvironmentUtils.isEnvironmentCodeEmpty(environmentCode)).isFalse(); + } + + @Test + void testGetDefaultEnvironmentCode() { + assertThat(EnvironmentUtils.getDefaultEnvironmentCode()).isEqualTo(-1L); + } + + @ParameterizedTest + @ValueSource(longs = {0, -1}) + void testGetEnvironmentCodeOrDefault_emptyEnvironmentCode(Long environmentCode) { + assertThat(EnvironmentUtils.getEnvironmentCodeOrDefault(environmentCode)).isEqualTo(-1L); + } + + @ParameterizedTest + @ValueSource(longs = {123}) + void testGetEnvironmentCodeOrDefault_nonEmptyEnvironmentCode(Long environmentCode) { + assertThat(EnvironmentUtils.getEnvironmentCodeOrDefault(environmentCode)).isEqualTo(environmentCode); + } + + @ParameterizedTest + @CsvSource(value = {",123", "-1,123"}) + void testGetEnvironmentCodeOrDefault_withDefaultValue_emptyEnvironmentCode(Long environmentCode, + Long defaultValue) { + assertThat(EnvironmentUtils.getEnvironmentCodeOrDefault(environmentCode, defaultValue)).isEqualTo(defaultValue); + } + + @ParameterizedTest + @CsvSource(value = {"1,123"}) + void testGetEnvironmentCodeOrDefault_withDefaultValue_nonEmptyEnvironmentCode(Long environmentCode, + Long defaultValue) { + assertThat(EnvironmentUtils.getEnvironmentCodeOrDefault(environmentCode, defaultValue)) + .isEqualTo(environmentCode); + } +} diff --git a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/TaskCacheUtilsTest.java b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/TaskCacheUtilsTest.java index 49d5a87bb6..ee88d1cd1a 100644 --- a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/TaskCacheUtilsTest.java +++ b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/TaskCacheUtilsTest.java @@ -20,7 +20,7 @@ package org.apache.dolphinscheduler.dao.utils; import org.apache.dolphinscheduler.common.enums.Flag; import org.apache.dolphinscheduler.common.utils.FileUtils; import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.enums.DataType; import org.apache.dolphinscheduler.plugin.task.api.enums.Direct; @@ -46,7 +46,7 @@ class TaskCacheUtilsTest { private TaskExecutionContext taskExecutionContext; - private StorageOperate storageOperate; + private StorageOperator storageOperator; @BeforeEach void setUp() { @@ -101,7 +101,7 @@ class TaskCacheUtilsTest { prepareParamsMap.put("a", property); taskExecutionContext.setPrepareParamsMap(prepareParamsMap); - storageOperate = Mockito.mock(StorageOperate.class); + storageOperator = Mockito.mock(StorageOperator.class); } @Test @@ -128,26 +128,26 @@ class TaskCacheUtilsTest { @Test void TestGetTaskInputVarPoolData() { - TaskCacheUtils.getTaskInputVarPoolData(taskInstance, taskExecutionContext, storageOperate); + TaskCacheUtils.getTaskInputVarPoolData(taskInstance, taskExecutionContext, storageOperator); // only a=aa and c=cc will influence the result, // b=bb is a fixed value, will be considered in task version // k=kk is not in task params, will be ignored String except = "[{\"prop\":\"a\",\"direct\":\"IN\",\"type\":\"VARCHAR\",\"value\":\"aa\"},{\"prop\":\"c\",\"direct\":\"IN\",\"type\":\"VARCHAR\",\"value\":\"cc\"}]"; Assertions.assertEquals(except, - TaskCacheUtils.getTaskInputVarPoolData(taskInstance, taskExecutionContext, storageOperate)); + TaskCacheUtils.getTaskInputVarPoolData(taskInstance, taskExecutionContext, storageOperator)); } @Test void TestGenerateCacheKey() { - String cacheKeyBase = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperate); + String cacheKeyBase = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperator); Property propertyI = new Property(); propertyI.setProp("i"); propertyI.setDirect(Direct.IN); propertyI.setType(DataType.VARCHAR); propertyI.setValue("ii"); taskExecutionContext.getPrepareParamsMap().put("i", propertyI); - String cacheKeyNew = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperate); + String cacheKeyNew = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperator); // i will not influence the result, because task instance not use it Assertions.assertEquals(cacheKeyBase, cacheKeyNew); @@ -157,17 +157,17 @@ class TaskCacheUtilsTest { propertyD.setType(DataType.VARCHAR); propertyD.setValue("dd"); taskExecutionContext.getPrepareParamsMap().put("i", propertyD); - String cacheKeyD = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperate); + String cacheKeyD = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperator); // d will influence the result, because task instance use it Assertions.assertNotEquals(cacheKeyBase, cacheKeyD); taskInstance.setTaskDefinitionVersion(100); - String cacheKeyE = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperate); + String cacheKeyE = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperator); // task definition version is changed, so cache key changed Assertions.assertNotEquals(cacheKeyD, cacheKeyE); taskInstance.setEnvironmentConfig("export PYTHON_LAUNCHER=/bin/python3"); - String cacheKeyF = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperate); + String cacheKeyF = TaskCacheUtils.generateCacheKey(taskInstance, taskExecutionContext, storageOperator); // EnvironmentConfig is changed, so cache key changed Assertions.assertNotEquals(cacheKeyE, cacheKeyF); } @@ -193,7 +193,7 @@ class TaskCacheUtilsTest { taskExecutionContext.setExecutePath("test"); taskExecutionContext.setTenantCode("aaa"); - String crc = TaskCacheUtils.getValCheckSum(property, taskExecutionContext, storageOperate); + String crc = TaskCacheUtils.getValCheckSum(property, taskExecutionContext, storageOperator); Assertions.assertEquals(crc, content); } diff --git a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/WorkerGroupUtilsTest.java b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/WorkerGroupUtilsTest.java new file mode 100644 index 0000000000..60bf74695d --- /dev/null +++ b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/utils/WorkerGroupUtilsTest.java @@ -0,0 +1,70 @@ +/* + * 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.dolphinscheduler.dao.utils; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +class WorkerGroupUtilsTest { + + @ParameterizedTest + @ValueSource(strings = {"", "default"}) + void testIsWorkerGroupEmpty_emptyWorkerGroup(String workerGroup) { + assertThat(WorkerGroupUtils.isWorkerGroupEmpty(workerGroup)).isTrue(); + } + + @ParameterizedTest + @ValueSource(strings = {"123", "default1"}) + void testIsWorkerGroupEmpty_nonEmptyWorkerGroup(String workerGroup) { + assertThat(WorkerGroupUtils.isWorkerGroupEmpty(workerGroup)).isFalse(); + } + + @ParameterizedTest + @ValueSource(strings = {"", "default"}) + void testGetWorkerGroupOrDefault_emptyWorkerGroup(String workerGroup) { + assertThat(WorkerGroupUtils.getWorkerGroupOrDefault(workerGroup)) + .isEqualTo(WorkerGroupUtils.getDefaultWorkerGroup()); + } + + @ParameterizedTest + @ValueSource(strings = {"test"}) + void testGetWorkerGroupOrDefault_nonEmptyWorkerGroup(String workerGroup) { + assertThat(WorkerGroupUtils.getWorkerGroupOrDefault(workerGroup)).isEqualTo(workerGroup); + } + + @ParameterizedTest + @CsvSource(value = {",test", "default,test"}) + void testGetWorkerGroupOrDefault_withDefaultValue_emptyWorkerGroup(String workerGroup, String defaultValue) { + assertThat(WorkerGroupUtils.getWorkerGroupOrDefault(workerGroup, defaultValue)).isEqualTo(defaultValue); + } + + @ParameterizedTest + @CsvSource(value = {"test1,test"}) + void testGetWorkerGroupOrDefault_withDefaultValue_nonEmptyWorkerGroup(String workerGroup, String defaultValue) { + assertThat(WorkerGroupUtils.getWorkerGroupOrDefault(workerGroup)).isEqualTo(workerGroup); + } + + @Test + void getDefaultWorkerGroup() { + assertThat(WorkerGroupUtils.getDefaultWorkerGroup()).isEqualTo("default"); + } +} diff --git a/dolphinscheduler-dao/src/test/resources/logback.xml b/dolphinscheduler-dao/src/test/resources/logback.xml new file mode 100644 index 0000000000..9a182a18ef --- /dev/null +++ b/dolphinscheduler-dao/src/test/resources/logback.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-api/src/main/java/org/apache/dolphinscheduler/plugin/datasource/api/utils/CommonUtils.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-api/src/main/java/org/apache/dolphinscheduler/plugin/datasource/api/utils/CommonUtils.java index 1c24785c2f..eb8f2ddef0 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-api/src/main/java/org/apache/dolphinscheduler/plugin/datasource/api/utils/CommonUtils.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-api/src/main/java/org/apache/dolphinscheduler/plugin/datasource/api/utils/CommonUtils.java @@ -29,7 +29,7 @@ import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.LOGIN_US import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.RESOURCE_UPLOAD_PATH; import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; +import org.apache.dolphinscheduler.common.enums.StorageType; import org.apache.dolphinscheduler.common.utils.PropertyUtils; import org.apache.commons.lang3.StringUtils; @@ -72,9 +72,9 @@ public class CommonUtils { */ public static boolean getKerberosStartupState() { String resUploadStartupType = PropertyUtils.getUpperCaseString(RESOURCE_STORAGE_TYPE); - ResUploadType resUploadType = ResUploadType.valueOf(resUploadStartupType); + StorageType storageType = StorageType.valueOf(resUploadStartupType); Boolean kerberosStartupState = PropertyUtils.getBoolean(HADOOP_SECURITY_AUTHENTICATION_STARTUP_STATE, false); - return resUploadType == ResUploadType.HDFS && kerberosStartupState; + return storageType == StorageType.HDFS && kerberosStartupState; } /** diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hive/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hive/security/UserGroupInformationFactory.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hive/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hive/security/UserGroupInformationFactory.java index 168ff3bdca..a39cdaf614 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hive/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hive/security/UserGroupInformationFactory.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-hive/src/main/java/org/apache/dolphinscheduler/plugin/datasource/hive/security/UserGroupInformationFactory.java @@ -20,7 +20,7 @@ package org.apache.dolphinscheduler.plugin.datasource.hive.security; import static org.apache.dolphinscheduler.common.constants.Constants.JAVA_SECURITY_KRB5_CONF; import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; +import org.apache.dolphinscheduler.common.enums.StorageType; import org.apache.dolphinscheduler.common.thread.ThreadUtils; import org.apache.dolphinscheduler.common.utils.PropertyUtils; @@ -120,10 +120,10 @@ public class UserGroupInformationFactory { public static boolean openKerberos() { String resUploadStartupType = PropertyUtils.getUpperCaseString(Constants.RESOURCE_STORAGE_TYPE); - ResUploadType resUploadType = ResUploadType.valueOf(resUploadStartupType); + StorageType storageType = StorageType.valueOf(resUploadStartupType); Boolean kerberosStartupState = PropertyUtils.getBoolean(Constants.HADOOP_SECURITY_AUTHENTICATION_STARTUP_STATE, false); - return resUploadType == ResUploadType.HDFS && kerberosStartupState; + return storageType == StorageType.HDFS && kerberosStartupState; } } diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-sqlserver/src/main/java/org/apache/dolphinscheduler/plugin/datasource/sqlserver/param/SQLServerDataSourceProcessor.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-sqlserver/src/main/java/org/apache/dolphinscheduler/plugin/datasource/sqlserver/param/SQLServerDataSourceProcessor.java index c3b73580dd..687d853fff 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-sqlserver/src/main/java/org/apache/dolphinscheduler/plugin/datasource/sqlserver/param/SQLServerDataSourceProcessor.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-sqlserver/src/main/java/org/apache/dolphinscheduler/plugin/datasource/sqlserver/param/SQLServerDataSourceProcessor.java @@ -35,6 +35,7 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import com.alibaba.druid.sql.parser.SQLParserUtils; import com.google.auto.service.AutoService; @@ -128,8 +129,10 @@ public class SQLServerDataSourceProcessor extends AbstractDataSourceProcessor { @Override public List splitAndRemoveComment(String sql) { - String cleanSQL = SQLParserUtils.removeComment(sql, com.alibaba.druid.DbType.sqlserver); - return SQLParserUtils.split(cleanSQL, com.alibaba.druid.DbType.sqlserver); + return SQLParserUtils.splitAndRemoveComment(sql, com.alibaba.druid.DbType.sqlserver) + .stream() + .map(subSql -> subSql.concat(";")) + .collect(Collectors.toList()); } private String transformOther(Map otherMap) { diff --git a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-sqlserver/src/test/java/org/apache/dolphinscheduler/plugin/datasource/sqlserver/param/SQLServerDataSourceProcessorTest.java b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-sqlserver/src/test/java/org/apache/dolphinscheduler/plugin/datasource/sqlserver/param/SQLServerDataSourceProcessorTest.java index de30e27e0b..7d34755d65 100644 --- a/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-sqlserver/src/test/java/org/apache/dolphinscheduler/plugin/datasource/sqlserver/param/SQLServerDataSourceProcessorTest.java +++ b/dolphinscheduler-datasource-plugin/dolphinscheduler-datasource-sqlserver/src/test/java/org/apache/dolphinscheduler/plugin/datasource/sqlserver/param/SQLServerDataSourceProcessorTest.java @@ -17,12 +17,15 @@ package org.apache.dolphinscheduler.plugin.datasource.sqlserver.param; +import static com.google.common.truth.Truth.assertThat; + import org.apache.dolphinscheduler.common.constants.DataSourceConstants; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.datasource.api.utils.PasswordUtils; import org.apache.dolphinscheduler.spi.enums.DbType; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; @@ -95,4 +98,32 @@ public class SQLServerDataSourceProcessorTest { Assertions.assertEquals(DataSourceConstants.SQLSERVER_VALIDATION_QUERY, sqlServerDatasourceProcessor.getValidationQuery()); } + + @Test + void splitAndRemoveComment_singleSelect() { + String sql = "select * from table;"; + List subSqls = sqlServerDatasourceProcessor.splitAndRemoveComment(sql); + assertThat(subSqls).hasSize(1); + assertThat(subSqls.get(0)).isEqualTo("select * from table;"); + } + + @Test + void splitAndRemoveComment_singleMerge() { + String sql = "MERGE\n" + + " [ TOP ( expression ) [ PERCENT ] ]\n" + + " [ INTO ] [ WITH ( ) ] [ [ AS ] table_alias ]\n" + + " USING [ [ AS ] table_alias ]\n" + + " ON \n" + + " [ WHEN MATCHED [ AND ]\n" + + " THEN ] [ ...n ]\n" + + " [ WHEN NOT MATCHED [ BY TARGET ] [ AND ]\n" + + " THEN ]\n" + + " [ WHEN NOT MATCHED BY SOURCE [ AND ]\n" + + " THEN ] [ ...n ]\n" + + " [ ]\n" + + " [ OPTION ( [ ,...n ] ) ];"; + List subSqls = sqlServerDatasourceProcessor.splitAndRemoveComment(sql); + assertThat(subSqls).hasSize(1); + assertThat(subSqls.get(0)).isEqualTo(sql); + } } diff --git a/dolphinscheduler-e2e/README.md b/dolphinscheduler-e2e/README.md index f2fbcead77..eb332de90e 100644 --- a/dolphinscheduler-e2e/README.md +++ b/dolphinscheduler-e2e/README.md @@ -97,3 +97,27 @@ class TenantE2ETest { - For UI tests, it's common that the pages might need some time to load, or the operations might need some time to complete, we can use `await().untilAsserted(() -> {})` to wait for the assertions. +## Local development + +### Mac M1 +Add VM options to the test configuration in IntelliJ IDEA: +``` +# In this mode you need to install docker desktop for mac and run it with locally +-Dm1_chip=true +``` + +### Running locally(without Docker) +``` +# In this mode you need to start frontend and backend services locally +-Dlocal=true +``` + +### Running locally(with Docker) +``` +# In this mode you only need to install docker locally +``` + +- To run the tests locally, you need to have the DolphinScheduler running locally. You should add `dolphinscheduler-e2e/pom.xml` to the maven project + Since it does not participate in project compilation, it is not in the main project. +- Running run test class `org.apache.dolphinscheduler.e2e.cases.UserE2ETest` in the IDE. After execution, the test video will be saved as mp4 in a local temporary directory. Such as + `/var/folders/hf/123/T/record-3123/PASSED-[engine:junit-jupiter]/[class:org.apache.dolphinscheduler.e2e.cases.UserE2ETest]-20240606-152333.mp4` diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/WorkflowJavaTaskE2ETest.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/WorkflowJavaTaskE2ETest.java index d61a9ddd2a..77c4e554bc 100644 --- a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/WorkflowJavaTaskE2ETest.java +++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/java/org/apache/dolphinscheduler/e2e/cases/WorkflowJavaTaskE2ETest.java @@ -92,8 +92,8 @@ public class WorkflowJavaTaskE2ETest { .goToNav(SecurityPage.class) .goToTab(UserPage.class); - new WebDriverWait(userPage.driver(), Duration.ofSeconds(20)).until(ExpectedConditions.visibilityOfElementLocated( - new By.ByClassName("name"))); + new WebDriverWait(userPage.driver(), Duration.ofSeconds(20)) + .until(ExpectedConditions.visibilityOfElementLocated(new By.ByClassName("name"))); userPage.update(user, user, email, phone, tenant) .goToNav(ProjectPage.class) diff --git a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java index 21c740952c..dcf22a2d6d 100644 --- a/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java +++ b/dolphinscheduler-e2e/dolphinscheduler-e2e-core/src/main/java/org/apache/dolphinscheduler/e2e/core/DolphinSchedulerExtension.java @@ -134,8 +134,8 @@ final class DolphinSchedulerExtension implements BeforeAllCallback, AfterAllCall private void setBrowserContainerByOsName() { DockerImageName imageName; - if (LOCAL_MODE && M1_CHIP_FLAG) { - imageName = DockerImageName.parse("seleniarm/standalone-chromium:4.1.2-20220227") + if (M1_CHIP_FLAG) { + imageName = DockerImageName.parse("seleniarm/standalone-chromium:124.0-chromedriver-124.0") .asCompatibleSubstituteFor("selenium/standalone-chrome"); browser = new BrowserWebDriverContainer<>(imageName) @@ -143,6 +143,7 @@ final class DolphinSchedulerExtension implements BeforeAllCallback, AfterAllCall .withCreateContainerCmdModifier(cmd -> cmd.withUser("root")) .withFileSystemBind(Constants.HOST_CHROME_DOWNLOAD_PATH.toFile().getAbsolutePath(), Constants.SELENIUM_CONTAINER_CHROME_DOWNLOAD_PATH) + .withRecordingMode(RECORD_ALL, record.toFile(), MP4) .withStartupTimeout(Duration.ofSeconds(300)); } else { browser = new BrowserWebDriverContainer<>() @@ -203,7 +204,7 @@ final class DolphinSchedulerExtension implements BeforeAllCallback, AfterAllCall .map(URL::getPath) .map(File::new) .collect(Collectors.toList()); - + ComposeContainer compose = new ComposeContainer(files) .withPull(true) .withTailChildContainers(true) @@ -213,7 +214,7 @@ final class DolphinSchedulerExtension implements BeforeAllCallback, AfterAllCall DOCKER_PORT, Wait.forListeningPort().withStartupTimeout(Duration.ofSeconds(300))) .withLogConsumer(serviceName, outputFrame -> LOGGER.info(outputFrame.getUtf8String())) .waitingFor(serviceName, Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(300))); - + return compose; } diff --git a/dolphinscheduler-e2e/pom.xml b/dolphinscheduler-e2e/pom.xml index c7ce90b1a6..6731240486 100644 --- a/dolphinscheduler-e2e/pom.xml +++ b/dolphinscheduler-e2e/pom.xml @@ -36,7 +36,7 @@ UTF-8 5.8.1 - 4.6.0 + 4.13.0 1.18.20 3.20.2 1.5.30 @@ -119,7 +119,7 @@ org.testcontainers testcontainers-bom - 1.19.3 + 1.19.8 import pom diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/pom.xml b/dolphinscheduler-extract/dolphinscheduler-extract-base/pom.xml index 9501d55706..0554ea3c3c 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/pom.xml +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/pom.xml @@ -47,6 +47,13 @@ dolphinscheduler-common ${project.version} + + + org.apache.dolphinscheduler + dolphinscheduler-meter + ${project.version} + + io.netty netty-all diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/RpcMethod.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/RpcMethod.java index cd1b778c9a..2dcf689ad8 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/RpcMethod.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/RpcMethod.java @@ -28,6 +28,6 @@ import java.lang.annotation.Target; @Documented public @interface RpcMethod { - long timeout() default 3000L; + long timeout() default -1; } diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/SyncRequestDto.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/SyncRequestDto.java new file mode 100644 index 0000000000..7df649cd57 --- /dev/null +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/SyncRequestDto.java @@ -0,0 +1,38 @@ +/* + * 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.dolphinscheduler.extract.base; + +import org.apache.dolphinscheduler.extract.base.protocal.Transporter; +import org.apache.dolphinscheduler.extract.base.utils.Host; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SyncRequestDto { + + private Host serverHost; + private Transporter transporter; + private long timeoutMillis; + +} diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/NettyClientHandler.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/NettyClientHandler.java index be570eb577..5f50e2441c 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/NettyClientHandler.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/NettyClientHandler.java @@ -77,7 +77,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter { .writeAndFlush(HeartBeatTransporter.getHeartBeatTransporter()) .addListener(ChannelFutureListener.CLOSE_ON_FAILURE); if (log.isDebugEnabled()) { - log.debug("Client send heart beat to: {}", ChannelUtils.getRemoteAddress(ctx.channel())); + log.info("Client send heartbeat to: {}", ctx.channel().remoteAddress()); } } else { super.userEventTriggered(ctx, evt); diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/NettyRemotingClient.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/NettyRemotingClient.java index 8ded68669d..4aea4d6dfe 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/NettyRemotingClient.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/NettyRemotingClient.java @@ -19,14 +19,17 @@ package org.apache.dolphinscheduler.extract.base.client; import org.apache.dolphinscheduler.common.thread.ThreadUtils; import org.apache.dolphinscheduler.extract.base.IRpcResponse; +import org.apache.dolphinscheduler.extract.base.SyncRequestDto; import org.apache.dolphinscheduler.extract.base.config.NettyClientConfig; import org.apache.dolphinscheduler.extract.base.exception.RemotingException; import org.apache.dolphinscheduler.extract.base.exception.RemotingTimeoutException; import org.apache.dolphinscheduler.extract.base.future.ResponseFuture; +import org.apache.dolphinscheduler.extract.base.metrics.ClientSyncDurationMetrics; +import org.apache.dolphinscheduler.extract.base.metrics.ClientSyncExceptionMetrics; +import org.apache.dolphinscheduler.extract.base.metrics.RpcMetrics; import org.apache.dolphinscheduler.extract.base.protocal.Transporter; import org.apache.dolphinscheduler.extract.base.protocal.TransporterDecoder; import org.apache.dolphinscheduler.extract.base.protocal.TransporterEncoder; -import org.apache.dolphinscheduler.extract.base.utils.Constants; import org.apache.dolphinscheduler.extract.base.utils.Host; import org.apache.dolphinscheduler.extract.base.utils.NettyUtils; @@ -97,8 +100,8 @@ public class NettyRemotingClient implements AutoCloseable { ch.pipeline() .addLast("client-idle-handler", new IdleStateHandler( - Constants.NETTY_CLIENT_HEART_BEAT_TIME, 0, + clientConfig.getHeartBeatIntervalMillis(), 0, TimeUnit.MILLISECONDS)) .addLast(new TransporterDecoder(), clientHandler, new TransporterEncoder()); @@ -107,38 +110,60 @@ public class NettyRemotingClient implements AutoCloseable { isStarted.compareAndSet(false, true); } - public IRpcResponse sendSync(final Host host, - final Transporter transporter, - final long timeoutMillis) throws InterruptedException, RemotingException { - final Channel channel = getOrCreateChannel(host); - if (channel == null) { - throw new RemotingException(String.format("connect to : %s fail", host)); - } + public IRpcResponse sendSync(SyncRequestDto syncRequestDto) throws RemotingException { + long start = System.currentTimeMillis(); + + final Host host = syncRequestDto.getServerHost(); + final Transporter transporter = syncRequestDto.getTransporter(); + final long timeoutMillis = syncRequestDto.getTimeoutMillis() < 0 ? clientConfig.getConnectTimeoutMillis() + : syncRequestDto.getTimeoutMillis(); final long opaque = transporter.getHeader().getOpaque(); - final ResponseFuture responseFuture = new ResponseFuture(opaque, timeoutMillis); - channel.writeAndFlush(transporter).addListener(future -> { - if (future.isSuccess()) { - responseFuture.setSendOk(true); - return; - } else { - responseFuture.setSendOk(false); + + try { + final Channel channel = getOrCreateChannel(host); + if (channel == null) { + throw new RemotingException(String.format("connect to : %s fail", host)); } - responseFuture.setCause(future.cause()); - responseFuture.putResponse(null); - log.error("Send Sync request {} to host {} failed", transporter, host, responseFuture.getCause()); - }); - /* - * sync wait for result - */ - IRpcResponse iRpcResponse = responseFuture.waitResponse(); - if (iRpcResponse == null) { - if (responseFuture.isSendOK()) { - throw new RemotingTimeoutException(host.toString(), timeoutMillis, responseFuture.getCause()); - } else { - throw new RemotingException(host.toString(), responseFuture.getCause()); + final ResponseFuture responseFuture = new ResponseFuture(opaque, timeoutMillis); + channel.writeAndFlush(transporter).addListener(future -> { + if (future.isSuccess()) { + responseFuture.setSendOk(true); + return; + } else { + responseFuture.setSendOk(false); + } + responseFuture.setCause(future.cause()); + responseFuture.putResponse(null); + log.error("Send Sync request {} to host {} failed", transporter, host, responseFuture.getCause()); + }); + /* + * sync wait for result + */ + IRpcResponse iRpcResponse = responseFuture.waitResponse(); + if (iRpcResponse == null) { + if (responseFuture.isSendOK()) { + throw new RemotingTimeoutException(host.toString(), timeoutMillis, responseFuture.getCause()); + } else { + throw new RemotingException(host.toString(), responseFuture.getCause()); + } } + return iRpcResponse; + } catch (Exception ex) { + ClientSyncExceptionMetrics clientSyncExceptionMetrics = ClientSyncExceptionMetrics + .of(syncRequestDto) + .withThrowable(ex); + RpcMetrics.recordClientSyncRequestException(clientSyncExceptionMetrics); + if (ex instanceof RemotingException) { + throw (RemotingException) ex; + } else { + throw new RemotingException(ex); + } + } finally { + ClientSyncDurationMetrics clientSyncDurationMetrics = ClientSyncDurationMetrics + .of(syncRequestDto) + .withMilliseconds(System.currentTimeMillis() - start); + RpcMetrics.recordClientSyncRequestDuration(clientSyncDurationMetrics); } - return iRpcResponse; } Channel getOrCreateChannel(Host host) { diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/SyncClientMethodInvoker.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/SyncClientMethodInvoker.java index 4731a22d0a..ccbdad945b 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/SyncClientMethodInvoker.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/client/SyncClientMethodInvoker.java @@ -20,6 +20,7 @@ package org.apache.dolphinscheduler.extract.base.client; import org.apache.dolphinscheduler.extract.base.IRpcResponse; import org.apache.dolphinscheduler.extract.base.RpcMethod; import org.apache.dolphinscheduler.extract.base.StandardRpcRequest; +import org.apache.dolphinscheduler.extract.base.SyncRequestDto; import org.apache.dolphinscheduler.extract.base.exception.MethodInvocationException; import org.apache.dolphinscheduler.extract.base.protocal.Transporter; import org.apache.dolphinscheduler.extract.base.protocal.TransporterHeader; @@ -41,8 +42,12 @@ class SyncClientMethodInvoker extends AbstractClientMethodInvoker { transporter.setBody(JsonSerializer.serialize(StandardRpcRequest.of(args))); transporter.setHeader(TransporterHeader.of(methodIdentifier)); - IRpcResponse iRpcResponse = - nettyRemotingClient.sendSync(serverHost, transporter, sync.timeout()); + SyncRequestDto syncRequestDto = SyncRequestDto.builder() + .timeoutMillis(sync.timeout()) + .transporter(transporter) + .serverHost(serverHost) + .build(); + IRpcResponse iRpcResponse = nettyRemotingClient.sendSync(syncRequestDto); if (!iRpcResponse.isSuccess()) { throw MethodInvocationException.of(iRpcResponse.getMessage()); } diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/config/NettyClientConfig.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/config/NettyClientConfig.java index a41a439b5c..a00ff540f4 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/config/NettyClientConfig.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/config/NettyClientConfig.java @@ -17,6 +17,8 @@ package org.apache.dolphinscheduler.extract.base.config; +import java.time.Duration; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -64,4 +66,14 @@ public class NettyClientConfig { @Builder.Default private int connectTimeoutMillis = 3000; + /** + * Will send {@link org.apache.dolphinscheduler.extract.base.protocal.HeartBeatTransporter} to netty server every + * heartBeatIntervalMillis, used to keep the {@link io.netty.channel.Channel} active. + */ + @Builder.Default + private long heartBeatIntervalMillis = Duration.ofSeconds(10).toMillis(); + + @Builder.Default + private int defaultRpcTimeoutMillis = 10_000; + } diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/config/NettyServerConfig.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/config/NettyServerConfig.java index 9d4a2ee3d2..cc0aa04f68 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/config/NettyServerConfig.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/config/NettyServerConfig.java @@ -17,6 +17,8 @@ package org.apache.dolphinscheduler.extract.base.config; +import java.time.Duration; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -37,7 +39,7 @@ public class NettyServerConfig { private int soBacklog = 1024; /** - * whether tpc delay + * whether tcp delay */ @Builder.Default private boolean tcpNoDelay = true; @@ -66,6 +68,12 @@ public class NettyServerConfig { @Builder.Default private int workerThread = Runtime.getRuntime().availableProcessors() * 2; + /** + * If done's receive any data from a {@link io.netty.channel.Channel} during 180s then will close it. + */ + @Builder.Default + private long connectionIdleTime = Duration.ofSeconds(60).toMillis(); + /** * listen port */ diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/context/ExecutionContext.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/ClientSyncDurationMetrics.java similarity index 51% rename from dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/context/ExecutionContext.java rename to dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/ClientSyncDurationMetrics.java index 8ad4013868..60124363b7 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/context/ExecutionContext.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/ClientSyncDurationMetrics.java @@ -15,13 +15,11 @@ * limitations under the License. */ -package org.apache.dolphinscheduler.server.master.dispatch.context; +package org.apache.dolphinscheduler.extract.base.metrics; -import static org.apache.dolphinscheduler.common.constants.Constants.DEFAULT_WORKER_GROUP; - -import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.extract.base.utils.Host; -import org.apache.dolphinscheduler.server.master.dispatch.enums.ExecutorType; +import org.apache.dolphinscheduler.common.utils.NetUtils; +import org.apache.dolphinscheduler.extract.base.SyncRequestDto; +import org.apache.dolphinscheduler.extract.base.protocal.Transporter; import lombok.AllArgsConstructor; import lombok.Builder; @@ -32,26 +30,27 @@ import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor -public class ExecutionContext { +public class ClientSyncDurationMetrics { - private Host host; + private Transporter transporter; - private TaskInstance taskInstance; + private long milliseconds; - private ExecutorType executorType; + @Builder.Default + private String clientHost = NetUtils.getHost(); - /** - * worker group - */ - private String workerGroup; + private String serverHost; - public ExecutionContext(ExecutorType executorType, TaskInstance taskInstance) { - this(executorType, DEFAULT_WORKER_GROUP, taskInstance); + public static ClientSyncDurationMetrics of(SyncRequestDto syncRequestDto) { + return ClientSyncDurationMetrics.builder() + .transporter(syncRequestDto.getTransporter()) + .serverHost(syncRequestDto.getServerHost().getIp()) + .build(); } - public ExecutionContext(ExecutorType executorType, String workerGroup, TaskInstance taskInstance) { - this.executorType = executorType; - this.workerGroup = workerGroup; - this.taskInstance = taskInstance; + public ClientSyncDurationMetrics withMilliseconds(long milliseconds) { + this.milliseconds = milliseconds; + return this; } + } diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/ClientSyncExceptionMetrics.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/ClientSyncExceptionMetrics.java new file mode 100644 index 0000000000..6f91132547 --- /dev/null +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/ClientSyncExceptionMetrics.java @@ -0,0 +1,56 @@ +/* + * 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.dolphinscheduler.extract.base.metrics; + +import org.apache.dolphinscheduler.common.utils.NetUtils; +import org.apache.dolphinscheduler.extract.base.SyncRequestDto; +import org.apache.dolphinscheduler.extract.base.protocal.Transporter; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ClientSyncExceptionMetrics { + + private Transporter transporter; + + private String clientHost; + + @Builder.Default + private String serverHost = NetUtils.getHost(); + + private Throwable throwable; + + public static ClientSyncExceptionMetrics of(SyncRequestDto syncRequestDto) { + return ClientSyncExceptionMetrics.builder() + .transporter(syncRequestDto.getTransporter()) + .build(); + + } + + public ClientSyncExceptionMetrics withThrowable(Throwable throwable) { + this.throwable = throwable; + return this; + } + +} diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/RpcMetrics.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/RpcMetrics.java new file mode 100644 index 0000000000..c2dbbfefa6 --- /dev/null +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/metrics/RpcMetrics.java @@ -0,0 +1,94 @@ +/* + * 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.dolphinscheduler.extract.base.metrics; + +import org.apache.dolphinscheduler.extract.base.protocal.Transporter; +import org.apache.dolphinscheduler.extract.base.protocal.TransporterHeader; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; + +public class RpcMetrics { + + private static final Map rpcRequestDurationTimer = new ConcurrentHashMap<>(); + + private static final Map rpcRequestExceptionCounter = new ConcurrentHashMap<>(); + + public static void recordClientSyncRequestException(ClientSyncExceptionMetrics clientSyncExceptionMetrics) { + recordClientSyncRequestException( + clientSyncExceptionMetrics.getThrowable(), + Optional.of(clientSyncExceptionMetrics) + .map(ClientSyncExceptionMetrics::getTransporter) + .map(Transporter::getHeader) + .map(TransporterHeader::getMethodIdentifier) + .orElseGet(() -> "unknown"), + clientSyncExceptionMetrics.getClientHost(), + clientSyncExceptionMetrics.getServerHost()); + } + + public static void recordClientSyncRequestException(final Throwable throwable, + final String methodName, + final String clientHost, + final String serverHost) { + final String exceptionType = throwable == null ? "unknown" : throwable.getClass().getSimpleName(); + final Counter counter = rpcRequestExceptionCounter.computeIfAbsent(exceptionType, + (et) -> Counter.builder("ds.rpc.client.sync.request.exception.count") + .tag("method_name", methodName) + .tag("client_host", clientHost) + .tag("server_host", serverHost) + .tag("exception_name", et) + .description("rpc sync request exception counter for exception type: " + et) + .register(Metrics.globalRegistry)); + counter.increment(); + } + + public static void recordClientSyncRequestDuration(ClientSyncDurationMetrics clientSyncDurationMetrics) { + recordClientSyncRequestDuration( + Optional.of(clientSyncDurationMetrics) + .map(ClientSyncDurationMetrics::getTransporter) + .map(Transporter::getHeader) + .map(TransporterHeader::getMethodIdentifier) + .orElseGet(() -> "unknown"), + clientSyncDurationMetrics.getMilliseconds(), + clientSyncDurationMetrics.getClientHost(), + clientSyncDurationMetrics.getServerHost()); + } + + public static void recordClientSyncRequestDuration(final String methodName, + final long milliseconds, + final String clientHost, + final String serverHost) { + rpcRequestDurationTimer.computeIfAbsent(methodName, + (method) -> Timer.builder("ds.rpc.client.sync.request.duration.time") + .tag("method_name", method) + .tag("client_host", clientHost) + .tag("server_host", serverHost) + .publishPercentiles(0.5, 0.75, 0.95, 0.99) + .publishPercentileHistogram() + .description("time cost of sync rpc request, unit ms") + .register(Metrics.globalRegistry)) + .record(milliseconds, TimeUnit.MILLISECONDS); + } + +} diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/server/JdkDynamicServerHandler.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/server/JdkDynamicServerHandler.java index f57ff0b609..4f9a7034c8 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/server/JdkDynamicServerHandler.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/server/JdkDynamicServerHandler.java @@ -38,6 +38,7 @@ import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; @Slf4j @@ -160,7 +161,11 @@ class JdkDynamicServerHandler extends ChannelInboundHandlerAdapter { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { - ctx.channel().close(); + IdleStateEvent event = (IdleStateEvent) evt; + if (event.state() == IdleState.READER_IDLE) { + log.warn("Not receive heart beat from: {}, will close the channel", ctx.channel().remoteAddress()); + ctx.close(); + } } else { super.userEventTriggered(ctx, evt); } diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/server/NettyRemotingServer.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/server/NettyRemotingServer.java index 9beeaced3d..9ebf802b1e 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/server/NettyRemotingServer.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/server/NettyRemotingServer.java @@ -22,7 +22,6 @@ import org.apache.dolphinscheduler.extract.base.config.NettyServerConfig; import org.apache.dolphinscheduler.extract.base.exception.RemoteException; import org.apache.dolphinscheduler.extract.base.protocal.TransporterDecoder; import org.apache.dolphinscheduler.extract.base.protocal.TransporterEncoder; -import org.apache.dolphinscheduler.extract.base.utils.Constants; import org.apache.dolphinscheduler.extract.base.utils.NettyUtils; import java.util.concurrent.ExecutorService; @@ -135,7 +134,7 @@ class NettyRemotingServer { .addLast("encoder", new TransporterEncoder()) .addLast("decoder", new TransporterDecoder()) .addLast("server-idle-handle", - new IdleStateHandler(0, 0, Constants.NETTY_SERVER_HEART_BEAT_TIME, TimeUnit.MILLISECONDS)) + new IdleStateHandler(serverConfig.getConnectionIdleTime(), 0, 0, TimeUnit.MILLISECONDS)) .addLast("handler", channelHandler); } diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/utils/Constants.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/utils/Constants.java index 76e3872d31..94b92b2539 100644 --- a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/utils/Constants.java +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/main/java/org/apache/dolphinscheduler/extract/base/utils/Constants.java @@ -35,10 +35,6 @@ public class Constants { public static final String SLASH = "/"; - public static final int NETTY_SERVER_HEART_BEAT_TIME = 1000 * 60 * 3 + 1000; - - public static final int NETTY_CLIENT_HEART_BEAT_TIME = 1000 * 6; - /** * charset */ diff --git a/dolphinscheduler-extract/dolphinscheduler-extract-base/src/test/java/org/apache/dolphinscheduler/extract/base/metrics/RpcMetricsTest.java b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/test/java/org/apache/dolphinscheduler/extract/base/metrics/RpcMetricsTest.java new file mode 100644 index 0000000000..352e218206 --- /dev/null +++ b/dolphinscheduler-extract/dolphinscheduler-extract-base/src/test/java/org/apache/dolphinscheduler/extract/base/metrics/RpcMetricsTest.java @@ -0,0 +1,70 @@ +/* + * 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.dolphinscheduler.extract.base.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import org.apache.dolphinscheduler.common.utils.NetUtils; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +class RpcMetricsTest { + + @BeforeEach + public void setup() { + Metrics.globalRegistry.clear(); + Metrics.addRegistry(new SimpleMeterRegistry()); + } + + @Test + void testRecordClientSyncRequestException() { + assertThat(Metrics.globalRegistry.find("ds.rpc.client.sync.request.exception.count").counter()).isNull(); + + String clientHost = NetUtils.getHost(); + String serverHost = NetUtils.getHost(); + + RpcMetrics.recordClientSyncRequestException( + new IllegalArgumentException("id is null"), "getById", clientHost, serverHost); + RpcMetrics.recordClientSyncRequestException( + new IllegalArgumentException("name is null"), "getByName", clientHost, serverHost); + RpcMetrics.recordClientSyncRequestException( + new IllegalArgumentException("age is null"), "getByAge", clientHost, serverHost); + RpcMetrics.recordClientSyncRequestException(new UnsupportedOperationException("update id is not supported"), + "updateById", clientHost, serverHost); + assertThat(Metrics.globalRegistry.find("ds.rpc.client.sync.request.exception.count").counter()).isNotNull(); + } + + @Test + void testRecordRpcRequestDuration() { + assertThat(Metrics.globalRegistry.find("ds.rpc.client.sync.request.duration.time").timer()).isNull(); + + String clientHost = NetUtils.getHost(); + String serverHost = NetUtils.getHost(); + + RpcMetrics.recordClientSyncRequestDuration("getById", 100, clientHost, serverHost); + RpcMetrics.recordClientSyncRequestDuration("getByName", 200, clientHost, serverHost); + RpcMetrics.recordClientSyncRequestDuration("getByAge", 300, clientHost, serverHost); + RpcMetrics.recordClientSyncRequestDuration("updateById", 400, clientHost, serverHost); + assertThat(Metrics.globalRegistry.find("ds.rpc.client.sync.request.duration.time").timer()).isNotNull(); + } + +} diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/MasterServer.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/MasterServer.java index 9b507500b9..92d7dcfaec 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/MasterServer.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/MasterServer.java @@ -108,7 +108,7 @@ public class MasterServer implements IStoppable { this.masterRPCServer.start(); // install task plugin - TaskPluginManager.loadPlugin(); + TaskPluginManager.loadTaskPlugin(); DataSourceProcessorProvider.initialize(); this.masterSlotManager.start(); diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/config/MasterConfig.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/config/MasterConfig.java index 20d3cccef3..d9e6ab3b7d 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/config/MasterConfig.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/config/MasterConfig.java @@ -96,10 +96,14 @@ public class MasterConfig implements Validator { private CommandFetchStrategy commandFetchStrategy = new CommandFetchStrategy(); - // ip:listenPort + /** + * The IP address and listening port of the master server in the format 'ip:listenPort'. + */ private String masterAddress; - // /nodes/master/ip:listenPort + /** + * The registry path for the master server in the format '/nodes/master/ip:listenPort'. + */ private String masterRegistryPath; @Override diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/LowerWeightRoundRobin.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/LowerWeightRoundRobin.java index d03fd59ada..9c3c4fd696 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/LowerWeightRoundRobin.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/LowerWeightRoundRobin.java @@ -25,10 +25,12 @@ import java.util.Collection; public class LowerWeightRoundRobin extends AbstractSelector { /** - * select + * Selects a HostWeight from a collection of HostWeight objects. + * The selection is based on the current weight of each HostWeight. + * The HostWeight with the smallest current weight is selected. * - * @param sources sources - * @return HostWeight + * @param sources A collection of HostWeight objects to select from. + * @return The selected HostWeight with the smallest current weight. */ @Override public HostWeight doSelect(Collection sources) { diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/RandomSelector.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/RandomSelector.java index 2b7488a370..a0f83232a2 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/RandomSelector.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/RandomSelector.java @@ -27,6 +27,14 @@ import java.util.concurrent.ThreadLocalRandom; */ public class RandomSelector extends AbstractSelector { + /** + * This method selects a HostWorker from a collection of HostWorker objects using a weighted random algorithm. + * The selection is based on the weight of each HostWorker. + * A random number is generated and the HostWorker whose weight spans this random number is selected. + * + * @param source A collection of HostWorker objects to select from. + * @return The selected HostWorker based on the weighted random algorithm. + */ @Override public HostWorker doSelect(final Collection source) { diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/RoundRobinSelector.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/RoundRobinSelector.java index 8f21acef6d..b47eff87b9 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/RoundRobinSelector.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/dispatch/host/assign/RoundRobinSelector.java @@ -73,6 +73,14 @@ public class RoundRobinSelector extends AbstractSelector { } + /** + * This method selects a HostWorker from a collection of HostWorker objects using a weighted round-robin algorithm. + * The selection is based on the current weight of each HostWorker. + * The HostWorker with the highest current weight is selected. + * + * @param source A collection of HostWorker objects to select from. + * @return The selected HostWorker with the highest current weight. + */ @Override public HostWorker doSelect(Collection source) { diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/event/WorkflowBlockStateEventHandler.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/event/WorkflowBlockStateEventHandler.java deleted file mode 100644 index 07f89fc544..0000000000 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/event/WorkflowBlockStateEventHandler.java +++ /dev/null @@ -1,59 +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.dolphinscheduler.server.master.event; - -import org.apache.dolphinscheduler.common.enums.StateEventType; -import org.apache.dolphinscheduler.common.utils.JSONUtils; -import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.plugin.task.api.parameters.BlockingParameters; -import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; - -import java.util.Optional; - -import lombok.extern.slf4j.Slf4j; - -import com.google.auto.service.AutoService; - -@AutoService(StateEventHandler.class) -@Slf4j -public class WorkflowBlockStateEventHandler implements StateEventHandler { - - @Override - public boolean handleStateEvent(WorkflowExecuteRunnable workflowExecuteRunnable, - StateEvent stateEvent) throws StateEventHandleError { - log.info("Handle workflow instance state block event"); - Optional taskInstanceOptional = - workflowExecuteRunnable.getTaskInstance(stateEvent.getTaskInstanceId()); - if (!taskInstanceOptional.isPresent()) { - throw new StateEventHandleError("Cannot find taskInstance from taskMap by taskInstanceId: " - + stateEvent.getTaskInstanceId()); - } - TaskInstance task = taskInstanceOptional.get(); - - BlockingParameters parameters = JSONUtils.parseObject(task.getTaskParams(), BlockingParameters.class); - if (parameters != null && parameters.isAlertWhenBlocking()) { - workflowExecuteRunnable.processBlock(); - } - return true; - } - - @Override - public StateEventType getEventType() { - return StateEventType.PROCESS_BLOCKED; - } -} diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/registry/ServerNodeManager.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/registry/ServerNodeManager.java index f066f0403d..d532168b2a 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/registry/ServerNodeManager.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/registry/ServerNodeManager.java @@ -25,6 +25,7 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.AlertDao; import org.apache.dolphinscheduler.dao.entity.WorkerGroup; import org.apache.dolphinscheduler.dao.mapper.WorkerGroupMapper; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.registry.api.Event; import org.apache.dolphinscheduler.registry.api.Event.Type; import org.apache.dolphinscheduler.registry.api.RegistryClient; @@ -36,7 +37,6 @@ import org.apache.dolphinscheduler.service.alert.ListenerEventAlertManager; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Arrays; @@ -273,8 +273,8 @@ public class ServerNodeManager implements InitializingBean { .filter(workerNodeInfo::containsKey).collect(Collectors.toSet()); tmpWorkerGroupMappings.put(workerGroupName, activeWorkerNodes); } - if (!tmpWorkerGroupMappings.containsKey(Constants.DEFAULT_WORKER_GROUP)) { - tmpWorkerGroupMappings.put(Constants.DEFAULT_WORKER_GROUP, workerNodeInfo.keySet()); + if (!tmpWorkerGroupMappings.containsKey(WorkerGroupUtils.getDefaultWorkerGroup())) { + tmpWorkerGroupMappings.put(WorkerGroupUtils.getDefaultWorkerGroup(), workerNodeInfo.keySet()); } } finally { workerNodeInfoReadLock.unlock(); @@ -307,9 +307,7 @@ public class ServerNodeManager implements InitializingBean { public Set getWorkerGroupNodes(String workerGroup) throws WorkerGroupNotFoundException { workerGroupReadLock.lock(); try { - if (StringUtils.isEmpty(workerGroup)) { - workerGroup = Constants.DEFAULT_WORKER_GROUP; - } + workerGroup = WorkerGroupUtils.getWorkerGroupOrDefault(workerGroup); Set nodes = workerGroupNodes.get(workerGroup); if (nodes == null) { throw new WorkerGroupNotFoundException(String.format("WorkerGroup: %s is invalidated", workerGroup)); diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/StreamTaskExecuteRunnable.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/StreamTaskExecuteRunnable.java index 2f61507e72..90fb25fcf0 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/StreamTaskExecuteRunnable.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/StreamTaskExecuteRunnable.java @@ -17,8 +17,6 @@ package org.apache.dolphinscheduler.server.master.runner; -import static org.apache.dolphinscheduler.common.constants.Constants.DEFAULT_WORKER_GROUP; - import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.Flag; import org.apache.dolphinscheduler.common.enums.Priority; @@ -31,6 +29,8 @@ import org.apache.dolphinscheduler.dao.entity.TaskDefinition; import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.dao.mapper.ProcessTaskRelationMapper; import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; +import org.apache.dolphinscheduler.dao.utils.EnvironmentUtils; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.extract.base.client.SingletonJdkDynamicRpcClientProxyFactory; import org.apache.dolphinscheduler.extract.master.transportor.StreamingTaskTriggerRequest; import org.apache.dolphinscheduler.extract.worker.ITaskInstanceExecutionEventAckListener; @@ -43,7 +43,6 @@ import org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; import org.apache.dolphinscheduler.plugin.task.api.model.Property; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; import org.apache.dolphinscheduler.plugin.task.api.utils.LogUtils; import org.apache.dolphinscheduler.plugin.task.api.utils.ParameterUtils; @@ -89,8 +88,6 @@ public class StreamTaskExecuteRunnable implements Runnable { protected ProcessTaskRelationMapper processTaskRelationMapper; - protected TaskPluginManager taskPluginManager; - private StreamTaskInstanceExecCacheManager streamTaskInstanceExecCacheManager; protected TaskDefinition taskDefinition; @@ -115,7 +112,6 @@ public class StreamTaskExecuteRunnable implements Runnable { this.processService = SpringApplicationContext.getBean(ProcessService.class); this.masterConfig = SpringApplicationContext.getBean(MasterConfig.class); this.workerTaskDispatcher = SpringApplicationContext.getBean(WorkerTaskDispatcher.class); - this.taskPluginManager = SpringApplicationContext.getBean(TaskPluginManager.class); this.processTaskRelationMapper = SpringApplicationContext.getBean(ProcessTaskRelationMapper.class); this.taskInstanceDao = SpringApplicationContext.getBean(TaskInstanceDao.class); this.streamTaskInstanceExecCacheManager = @@ -270,12 +266,11 @@ public class StreamTaskExecuteRunnable implements Runnable { // task dry run flag taskInstance.setDryRun(taskExecuteStartMessage.getDryRun()); - taskInstance.setWorkerGroup(StringUtils.isBlank(taskDefinition.getWorkerGroup()) ? DEFAULT_WORKER_GROUP - : taskDefinition.getWorkerGroup()); - taskInstance.setEnvironmentCode( - taskDefinition.getEnvironmentCode() == 0 ? -1 : taskDefinition.getEnvironmentCode()); + taskInstance.setWorkerGroup(WorkerGroupUtils.getWorkerGroupOrDefault(taskDefinition.getWorkerGroup())); + taskInstance + .setEnvironmentCode(EnvironmentUtils.getEnvironmentCodeOrDefault(taskDefinition.getEnvironmentCode())); - if (!taskInstance.getEnvironmentCode().equals(-1L)) { + if (!EnvironmentUtils.isEnvironmentCodeEmpty(taskInstance.getEnvironmentCode())) { Environment environment = processService.findEnvironmentByCode(taskInstance.getEnvironmentCode()); if (Objects.nonNull(environment) && StringUtils.isNotEmpty(environment.getConfig())) { taskInstance.setEnvironmentConfig(environment.getConfig()); @@ -312,14 +307,11 @@ public class StreamTaskExecuteRunnable implements Runnable { return null; } - TaskChannel taskChannel = taskPluginManager.getTaskChannel(taskInstance.getTaskType()); - ResourceParametersHelper resources = taskChannel.getResources(taskInstance.getTaskParams()); + TaskChannel taskChannel = TaskPluginManager.getTaskChannel(taskInstance.getTaskType()); + ResourceParametersHelper resources = taskChannel.parseParameters(taskInstance.getTaskParams()).getResources(); - AbstractParameters baseParam = taskPluginManager.getParameters( - ParametersNode.builder() - .taskType(taskInstance.getTaskType()) - .taskParams(taskInstance.getTaskParams()) - .build()); + AbstractParameters baseParam = + TaskPluginManager.parseTaskParameters(taskInstance.getTaskType(), taskInstance.getTaskParams()); Map propertyMap = paramParsingPreparation(taskInstance, baseParam); TaskExecutionContext taskExecutionContext = TaskExecutionContextBuilder.get() .buildWorkflowInstanceHost(masterConfig.getMasterAddress()) diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/TaskExecutionContextFactory.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/TaskExecutionContextFactory.java index b3b0cf67ea..da4f5308d0 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/TaskExecutionContextFactory.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/TaskExecutionContextFactory.java @@ -49,7 +49,6 @@ import org.apache.dolphinscheduler.plugin.task.api.model.JdbcInfo; import org.apache.dolphinscheduler.plugin.task.api.model.Property; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; import org.apache.dolphinscheduler.plugin.task.api.parameters.K8sTaskParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.plugin.task.api.parameters.dataquality.DataQualityParameters; import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.AbstractResourceParameters; import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.DataSourceParameters; @@ -74,7 +73,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import lombok.extern.slf4j.Slf4j; @@ -102,16 +100,15 @@ public class TaskExecutionContextFactory { public TaskExecutionContext createTaskExecutionContext(TaskInstance taskInstance) throws TaskExecutionContextCreateException { ProcessInstance workflowInstance = taskInstance.getProcessInstance(); - ResourceParametersHelper resources = - Optional.ofNullable(TaskPluginManager.getTaskChannel(taskInstance.getTaskType())) - .map(taskChannel -> taskChannel.getResources(taskInstance.getTaskParams())) - .orElse(null); + ResourceParametersHelper resources = TaskPluginManager.getTaskChannel(taskInstance.getTaskType()) + .parseParameters(taskInstance.getTaskParams()) + .getResources(); setTaskResourceInfo(resources); Map businessParamsMap = curingParamsService.preBuildBusinessParams(workflowInstance); - AbstractParameters baseParam = TaskPluginManager.getParameters(ParametersNode.builder() - .taskType(taskInstance.getTaskType()).taskParams(taskInstance.getTaskParams()).build()); + AbstractParameters baseParam = + TaskPluginManager.parseTaskParameters(taskInstance.getTaskType(), taskInstance.getTaskParams()); Map propertyMap = curingParamsService.paramParsingPreparation(taskInstance, baseParam, workflowInstance); TaskExecutionContext taskExecutionContext = TaskExecutionContextBuilder.get() diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteRunnable.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteRunnable.java index c35fd9991f..c58d741d41 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteRunnable.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteRunnable.java @@ -25,9 +25,9 @@ import static org.apache.dolphinscheduler.common.constants.CommandKeyConstants.C import static org.apache.dolphinscheduler.common.constants.CommandKeyConstants.CMD_PARAM_START_NODES; import static org.apache.dolphinscheduler.common.constants.CommandKeyConstants.CMD_PARAM_START_PARAMS; import static org.apache.dolphinscheduler.common.constants.Constants.COMMA; -import static org.apache.dolphinscheduler.common.constants.Constants.DEFAULT_WORKER_GROUP; import static org.apache.dolphinscheduler.common.constants.DateConstants.YYYY_MM_DD_HH_MM_SS; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_BLOCKING; +import static org.apache.dolphinscheduler.dao.utils.EnvironmentUtils.getEnvironmentCodeOrDefault; +import static org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils.getWorkerGroupOrDefault; import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.CommandType; @@ -51,15 +51,17 @@ import org.apache.dolphinscheduler.dao.entity.Schedule; import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; +import org.apache.dolphinscheduler.dao.utils.EnvironmentUtils; import org.apache.dolphinscheduler.dao.utils.TaskCacheUtils; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.extract.base.client.SingletonJdkDynamicRpcClientProxyFactory; import org.apache.dolphinscheduler.extract.worker.ITaskInstanceOperator; import org.apache.dolphinscheduler.extract.worker.transportor.UpdateWorkflowHostRequest; import org.apache.dolphinscheduler.extract.worker.transportor.UpdateWorkflowHostResponse; import org.apache.dolphinscheduler.plugin.task.api.enums.DependResult; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; -import org.apache.dolphinscheduler.plugin.task.api.parameters.SwitchParameters; import org.apache.dolphinscheduler.plugin.task.api.utils.LogUtils; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import org.apache.dolphinscheduler.plugin.task.api.utils.VarPoolUtils; import org.apache.dolphinscheduler.server.master.config.MasterConfig; import org.apache.dolphinscheduler.server.master.event.StateEvent; @@ -73,7 +75,6 @@ import org.apache.dolphinscheduler.server.master.graph.IWorkflowGraph; import org.apache.dolphinscheduler.server.master.metrics.TaskMetrics; import org.apache.dolphinscheduler.server.master.runner.execute.DefaultTaskExecuteRunnableFactory; import org.apache.dolphinscheduler.server.master.runner.taskgroup.TaskGroupCoordinator; -import org.apache.dolphinscheduler.server.master.utils.TaskUtils; import org.apache.dolphinscheduler.server.master.utils.WorkflowInstanceUtils; import org.apache.dolphinscheduler.service.alert.ListenerEventAlertManager; import org.apache.dolphinscheduler.service.alert.ProcessAlertManager; @@ -297,7 +298,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { stateEvent, stateEventHandleException); ThreadUtils.sleep(Constants.SLEEP_TIME_MILLIS); - } catch (Exception e) { + } catch (Throwable e) { // we catch the exception here, since if the state event handle failed, the state event will still // keep // in the stateEvents queue. @@ -378,25 +379,24 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { if (taskInstance.getIsCache().equals(Flag.YES)) { saveCacheTaskInstance(taskInstance); } - if (!workflowInstance.isBlocked()) { - submitPostNode(taskInstance.getTaskCode()); - } + submitPostNode(taskInstance.getTaskCode()); } else if (taskInstance.taskCanRetry() && !workflowInstance.getState().isReadyStop()) { // retry task log.info("Retry taskInstance taskInstance state: {}", taskInstance.getState()); retryTaskInstance(taskInstance); - } else if (taskInstance.getState().isFailure()) { + } else if (taskInstance.getState().isFailure() || taskInstance.getState().isKill() + || taskInstance.getState().isStop()) { completeTaskSet.add(taskInstance.getTaskCode()); - ProjectUser projectUser = - processService.queryProjectWithUserByProcessInstanceId(workflowInstance.getId()); - listenerEventAlertManager.publishTaskFailListenerEvent(workflowInstance, taskInstance, projectUser); + listenerEventAlertManager.publishTaskFailListenerEvent(workflowInstance, taskInstance); + if (isTaskNeedPutIntoErrorMap(taskInstance)) { + errorTaskMap.put(taskInstance.getTaskCode(), taskInstance.getId()); + } // There are child nodes and the failure policy is: CONTINUE if (workflowInstance.getFailureStrategy() == FailureStrategy.CONTINUE && DagHelper.haveAllNodeAfterNode( taskInstance.getTaskCode(), workflowExecuteContext.getWorkflowGraph().getDag())) { submitPostNode(taskInstance.getTaskCode()); } else { - errorTaskMap.put(taskInstance.getTaskCode(), taskInstance.getId()); if (workflowInstance.getFailureStrategy() == FailureStrategy.END) { killAllTasks(); } @@ -805,10 +805,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { completeTaskSet.add(task.getTaskCode()); continue; } - if (task.isConditionsTask() || DagHelper.haveConditionsAfterNode(task.getTaskCode(), - workflowExecuteContext.getWorkflowGraph().getDag())) { - continue; - } + if (task.taskCanRetry()) { if (task.getState().isNeedFaultTolerance()) { log.info("TaskInstance needs fault tolerance, will be added to standby list."); @@ -824,7 +821,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { } continue; } - if (task.getState().isFailure()) { + if (isTaskNeedPutIntoErrorMap(task)) { errorTaskMap.put(task.getTaskCode(), task.getId()); } } finally { @@ -949,16 +946,6 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { if (!taskInstance.getState().isFinished()) { taskExecuteRunnable.dispatch(); } else { - if (workflowExecuteContext.getWorkflowInstance().isBlocked()) { - TaskStateEvent processBlockEvent = TaskStateEvent.builder() - .processInstanceId(workflowExecuteContext.getWorkflowInstance().getId()) - .taskInstanceId(taskInstance.getId()) - .status(taskInstance.getState()) - .type(StateEventType.PROCESS_BLOCKED) - .build(); - this.stateEvents.add(processBlockEvent); - } - TaskStateEvent taskStateChangeEvent = TaskStateEvent.builder() .processInstanceId(workflowExecuteContext.getWorkflowInstance().getId()) .taskInstanceId(taskInstance.getId()) @@ -1095,7 +1082,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { taskInstance.setRetryInterval(taskNode.getRetryInterval()); // set task param - taskInstance.setTaskParams(taskNode.getTaskParams()); + taskInstance.setTaskParams(taskNode.getParams()); // set task group and priority taskInstance.setTaskGroupId(taskNode.getTaskGroupId()); @@ -1114,25 +1101,22 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { taskInstance.setTaskInstancePriority(taskNode.getTaskInstancePriority()); } - String processWorkerGroup = processInstance.getWorkerGroup(); - processWorkerGroup = StringUtils.isBlank(processWorkerGroup) ? DEFAULT_WORKER_GROUP : processWorkerGroup; - String taskWorkerGroup = - StringUtils.isBlank(taskNode.getWorkerGroup()) ? processWorkerGroup : taskNode.getWorkerGroup(); + String processWorkerGroup = getWorkerGroupOrDefault(processInstance.getWorkerGroup()); + String taskWorkerGroup = getWorkerGroupOrDefault(taskNode.getWorkerGroup()); - Long processEnvironmentCode = - Objects.isNull(processInstance.getEnvironmentCode()) ? -1 : processInstance.getEnvironmentCode(); - Long taskEnvironmentCode = - Objects.isNull(taskNode.getEnvironmentCode()) ? processEnvironmentCode : taskNode.getEnvironmentCode(); + Long processEnvironmentCode = getEnvironmentCodeOrDefault(processInstance.getEnvironmentCode()); + Long taskEnvironmentCode = getEnvironmentCodeOrDefault(taskNode.getEnvironmentCode()); - if (!processWorkerGroup.equals(DEFAULT_WORKER_GROUP) && taskWorkerGroup.equals(DEFAULT_WORKER_GROUP)) { + if (WorkerGroupUtils.isWorkerGroupEmpty(taskWorkerGroup)) { + // If the task workerGroup is empty, then use the workflow workerGroup/environment taskInstance.setWorkerGroup(processWorkerGroup); - taskInstance.setEnvironmentCode(processEnvironmentCode); + taskInstance.setEnvironmentCode(getEnvironmentCodeOrDefault(taskEnvironmentCode, processEnvironmentCode)); } else { taskInstance.setWorkerGroup(taskWorkerGroup); - taskInstance.setEnvironmentCode(taskEnvironmentCode); + taskInstance.setEnvironmentCode(getEnvironmentCodeOrDefault(taskEnvironmentCode, processEnvironmentCode)); } - if (!taskInstance.getEnvironmentCode().equals(-1L)) { + if (!EnvironmentUtils.isEnvironmentCodeEmpty(taskInstance.getEnvironmentCode())) { Environment environment = processService.findEnvironmentByCode(taskInstance.getEnvironmentCode()); if (Objects.nonNull(environment) && StringUtils.isNotEmpty(environment.getConfig())) { taskInstance.setEnvironmentConfig(environment.getConfig()); @@ -1219,12 +1203,12 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { || state == TaskExecutionStatus.DISPATCH || state == TaskExecutionStatus.SUBMITTED_SUCCESS || state == TaskExecutionStatus.DELAY_EXECUTION) { - // try to take over task instance - if (state == TaskExecutionStatus.SUBMITTED_SUCCESS || state == TaskExecutionStatus.DELAY_EXECUTION - || state == TaskExecutionStatus.DISPATCH) { + if (state == TaskExecutionStatus.SUBMITTED_SUCCESS + || state == TaskExecutionStatus.DELAY_EXECUTION) { // The taskInstance is not in running, directly takeover it } else if (tryToTakeOverTaskInstance(existTaskInstance)) { - log.info("Success take over task {}", existTaskInstance.getName()); + // If the taskInstance has already dispatched to worker then will try to take-over it + log.info("Success take over task {} -> status: {}", existTaskInstance.getName(), state); continue; } else { // set the task instance state to fault tolerance @@ -1277,7 +1261,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { private boolean tryToTakeOverTaskInstance(TaskInstance taskInstance) { ProcessInstance workflowInstance = workflowExecuteContext.getWorkflowInstance(); - if (TaskUtils.isMasterTask(taskInstance.getTaskType())) { + if (TaskTypeUtils.isLogicTask(taskInstance.getTaskType())) { return false; } try { @@ -1343,13 +1327,14 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { if (depTaskState.isKill()) { return DependResult.NON_EXEC; } - // ignore task state if current task is block - if (taskNode.isBlockingTask()) { + + // always return success if current task is condition + if (TaskTypeUtils.isConditionTask(taskNode.getType())) { continue; } - // always return success if current task is condition - if (taskNode.isConditionsTask()) { + // always return success if current task is switch + if (TaskTypeUtils.isSwitchTask(taskNode.getType())) { continue; } @@ -1394,7 +1379,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { private boolean dependTaskSuccess(Long dependNodeCode, Long nextNodeCode) { DAG dag = workflowExecuteContext.getWorkflowGraph().getDag(); TaskNode dependentNode = dag.getNode(dependNodeCode); - if (dependentNode.isConditionsTask()) { + if (TaskTypeUtils.isConditionTask(dependentNode.getType())) { // condition task need check the branch to run List nextTaskList = DagHelper.parseConditionTask(dependNodeCode, skipTaskNodeMap, dag, getCompleteTaskInstanceMap()); @@ -1407,12 +1392,6 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { } return true; } - if (dependentNode.isSwitchTask()) { - TaskInstance dependentTaskInstance = taskInstanceMap.get(validTaskMap.get(dependentNode.getCode())); - SwitchParameters switchParameters = dependentTaskInstance.getSwitchDependency(); - return switchParameters.getDependTaskList().get(switchParameters.getResultConditionLocation()).getNextNode() - .contains(nextNodeCode); - } Optional existTaskInstanceOptional = getTaskInstance(dependNodeCode); if (!existTaskInstanceOptional.isPresent()) { return false; @@ -1452,8 +1431,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { */ private WorkflowExecutionStatus runningState(WorkflowExecutionStatus state) { if (state == WorkflowExecutionStatus.READY_STOP || state == WorkflowExecutionStatus.READY_PAUSE - || state == WorkflowExecutionStatus.READY_BLOCK || - state == WorkflowExecutionStatus.DELAY_EXECUTION) { + || state == WorkflowExecutionStatus.DELAY_EXECUTION) { // if the running task is not completed, the state remains unchanged return state; } else { @@ -1512,7 +1490,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { } List pauseList = getCompleteTaskByState(TaskExecutionStatus.PAUSE); - if (CollectionUtils.isNotEmpty(pauseList) || workflowInstance.isBlocked() || !isComplementEnd() + if (CollectionUtils.isNotEmpty(pauseList) || !isComplementEnd() || standByTaskInstancePriorityQueue.size() > 0) { return WorkflowExecutionStatus.PAUSE; } else { @@ -1520,30 +1498,6 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { } } - /** - * prepare for block - * if process has tasks still running, pause them - * if readyToSubmitTaskQueue is not empty, kill them - * else return block status directly - * - * @return ExecutionStatus - */ - private WorkflowExecutionStatus processReadyBlock() { - if (taskExecuteRunnableMap.size() > 0) { - for (DefaultTaskExecuteRunnable taskExecuteRunnable : taskExecuteRunnableMap.values()) { - if (!TASK_TYPE_BLOCKING.equals(taskExecuteRunnable.getTaskInstance().getTaskType())) { - taskExecuteRunnable.pause(); - } - } - } - if (standByTaskInstancePriorityQueue.size() > 0) { - for (Iterator iter = standByTaskInstancePriorityQueue.iterator(); iter.hasNext();) { - iter.next().setState(TaskExecutionStatus.PAUSE); - } - } - return WorkflowExecutionStatus.BLOCK; - } - /** * generate the latest process instance status by the tasks state * @@ -1559,13 +1513,6 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { return executionStatus; } - // block - if (state == WorkflowExecutionStatus.READY_BLOCK) { - WorkflowExecutionStatus executionStatus = processReadyBlock(); - log.info("The workflowInstance is ready to block, the workflowInstance status is {}", executionStatus); - return executionStatus; - } - // pause if (state == WorkflowExecutionStatus.READY_PAUSE) { WorkflowExecutionStatus executionStatus = processReadyPause(); @@ -2015,6 +1962,24 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { } } + /** + * Whether the task instance need to put into {@link #errorTaskMap}. + * Only the task instance is failed or killed, and it is parent of condition task. + * Then it should be put into {@link #errorTaskMap}. + *

Once a task instance is put into {@link #errorTaskMap}, it will be thought as failed and make the workflow be failed. + */ + private boolean isTaskNeedPutIntoErrorMap(TaskInstance taskInstance) { + if (!taskInstance.getState().isFailure() && !taskInstance.getState().isStop() + && !taskInstance.getState().isKill()) { + return false; + } + TaskNode taskNode = workflowExecuteContext.getWorkflowGraph().getTaskNodeByCode(taskInstance.getTaskCode()); + if (DagHelper.haveConditionsAfterNode(taskNode.getCode(), workflowExecuteContext.getWorkflowGraph().getDag())) { + return false; + } + return true; + } + private enum WorkflowRunnableStatus { CREATED, INITIALIZE_QUEUE, STARTED, ; @@ -2022,7 +1987,7 @@ public class WorkflowExecuteRunnable implements IWorkflowExecuteRunnable { } private void sendTaskLogOnMasterToRemoteIfNeeded(TaskInstance taskInstance) { - if (RemoteLogUtils.isRemoteLoggingEnable() && TaskUtils.isMasterTask(taskInstance.getTaskType())) { + if (RemoteLogUtils.isRemoteLoggingEnable() && TaskTypeUtils.isLogicTask(taskInstance.getTaskType())) { RemoteLogUtils.sendRemoteLog(taskInstance.getLogPath()); log.info("Master sends task log {} to remote storage asynchronously.", taskInstance.getLogPath()); } diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/dispatcher/TaskDispatchFactory.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/dispatcher/TaskDispatchFactory.java index 52469fb54c..9943ee1b5b 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/dispatcher/TaskDispatchFactory.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/dispatcher/TaskDispatchFactory.java @@ -18,7 +18,7 @@ package org.apache.dolphinscheduler.server.master.runner.dispatcher; import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.server.master.utils.TaskUtils; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import lombok.extern.slf4j.Slf4j; @@ -36,7 +36,10 @@ public class TaskDispatchFactory { private WorkerTaskDispatcher workerTaskDispatcher; public TaskDispatcher getTaskDispatcher(String taskType) { - return TaskUtils.isMasterTask(taskType) ? masterTaskDispatcher : workerTaskDispatcher; + if (TaskTypeUtils.isLogicTask(taskType)) { + return masterTaskDispatcher; + } + return workerTaskDispatcher; } public TaskDispatcher getTaskDispatcher(TaskInstance taskInstance) { diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/operator/TaskExecuteRunnableOperatorManager.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/operator/TaskExecuteRunnableOperatorManager.java index 1b92f5e75c..0a67e801dc 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/operator/TaskExecuteRunnableOperatorManager.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/operator/TaskExecuteRunnableOperatorManager.java @@ -17,8 +17,8 @@ package org.apache.dolphinscheduler.server.master.runner.operator; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import org.apache.dolphinscheduler.server.master.runner.DefaultTaskExecuteRunnable; -import org.apache.dolphinscheduler.server.master.utils.TaskUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -51,28 +51,29 @@ public class TaskExecuteRunnableOperatorManager { private LogicTaskExecuteRunnableTimeoutOperator logicTaskTimeoutOperator; public TaskExecuteRunnableOperator getTaskKillOperator(DefaultTaskExecuteRunnable defaultTaskExecuteRunnable) { - if (TaskUtils.isMasterTask(defaultTaskExecuteRunnable.getTaskInstance().getTaskType())) { + if (TaskTypeUtils.isLogicTask(defaultTaskExecuteRunnable.getTaskInstance().getTaskType())) { return logicTaskKillOperator; } return taskKillOperator; } public TaskExecuteRunnableOperator getTaskPauseOperator(DefaultTaskExecuteRunnable defaultTaskExecuteRunnable) { - if (TaskUtils.isMasterTask(defaultTaskExecuteRunnable.getTaskInstance().getTaskType())) { + if (TaskTypeUtils.isLogicTask(defaultTaskExecuteRunnable.getTaskInstance().getTaskType())) { + return logicTaskPauseOperator; } return taskPauseOperator; } public TaskExecuteRunnableOperator getTaskDispatchOperator(DefaultTaskExecuteRunnable defaultTaskExecuteRunnable) { - if (TaskUtils.isMasterTask(defaultTaskExecuteRunnable.getTaskInstance().getTaskType())) { + if (TaskTypeUtils.isLogicTask(defaultTaskExecuteRunnable.getTaskInstance().getTaskType())) { return logicTaskDispatchOperator; } return taskDispatchOperator; } public TaskExecuteRunnableOperator getTaskTimeoutOperator(DefaultTaskExecuteRunnable defaultTaskExecuteRunnable) { - if (TaskUtils.isMasterTask(defaultTaskExecuteRunnable.getTaskInstance().getTaskType())) { + if (TaskTypeUtils.isLogicTask(defaultTaskExecuteRunnable.getTaskInstance().getTaskType())) { return logicTaskTimeoutOperator; } return taskTimeoutOperator; diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/BaseSyncLogicTask.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/BaseSyncLogicTask.java index 10a4ec1e7c..064e054ed3 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/BaseSyncLogicTask.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/BaseSyncLogicTask.java @@ -18,9 +18,12 @@ package org.apache.dolphinscheduler.server.master.runner.task; import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.server.master.exception.LogicTaskInitializeException; import org.apache.dolphinscheduler.server.master.exception.MasterTaskExecuteException; +import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; import lombok.extern.slf4j.Slf4j; @@ -28,10 +31,20 @@ import lombok.extern.slf4j.Slf4j; public abstract class BaseSyncLogicTask implements ISyncLogicTask { protected final TaskExecutionContext taskExecutionContext; + + protected final WorkflowExecuteRunnable workflowExecuteRunnable; + protected final TaskInstance taskInstance; protected final T taskParameters; - protected BaseSyncLogicTask(TaskExecutionContext taskExecutionContext, T taskParameters) { + protected BaseSyncLogicTask(WorkflowExecuteRunnable workflowExecuteRunnable, + TaskExecutionContext taskExecutionContext, + T taskParameters) throws LogicTaskInitializeException { this.taskExecutionContext = taskExecutionContext; + this.workflowExecuteRunnable = workflowExecuteRunnable; + this.taskInstance = + workflowExecuteRunnable.getTaskInstance(taskExecutionContext.getTaskInstanceId()).orElseThrow( + () -> new LogicTaskInitializeException( + "Cannot find the task instance in workflow execute runnable")); this.taskParameters = taskParameters; log.info("Success initialize task parameters: \n{}", JSONUtils.toPrettyJsonString(taskParameters)); } diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/blocking/BlockingLogicTask.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/blocking/BlockingLogicTask.java deleted file mode 100644 index acc05aaf2d..0000000000 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/blocking/BlockingLogicTask.java +++ /dev/null @@ -1,139 +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.dolphinscheduler.server.master.runner.task.blocking; - -import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.BlockingOpportunity; -import org.apache.dolphinscheduler.common.enums.WorkflowExecutionStatus; -import org.apache.dolphinscheduler.common.utils.JSONUtils; -import org.apache.dolphinscheduler.dao.entity.ProcessInstance; -import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; -import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; -import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; -import org.apache.dolphinscheduler.plugin.task.api.enums.DependResult; -import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; -import org.apache.dolphinscheduler.plugin.task.api.model.DependentItem; -import org.apache.dolphinscheduler.plugin.task.api.model.DependentTaskModel; -import org.apache.dolphinscheduler.plugin.task.api.parameters.BlockingParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameters; -import org.apache.dolphinscheduler.plugin.task.api.utils.DependentUtils; -import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; -import org.apache.dolphinscheduler.server.master.exception.MasterTaskExecuteException; -import org.apache.dolphinscheduler.server.master.runner.task.BaseSyncLogicTask; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import lombok.extern.slf4j.Slf4j; - -import com.fasterxml.jackson.core.type.TypeReference; - -@Slf4j -public class BlockingLogicTask extends BaseSyncLogicTask { - - public static final String TASK_TYPE = "BLOCKING"; - - private final ProcessInstanceExecCacheManager processInstanceExecCacheManager; - - private final ProcessInstanceDao processInstanceDao; - - private final TaskInstanceDao taskInstanceDao; - - public BlockingLogicTask(TaskExecutionContext taskExecutionContext, - ProcessInstanceExecCacheManager processInstanceExecCacheManager, - ProcessInstanceDao processInstanceDao, - TaskInstanceDao taskInstanceDao) { - super(taskExecutionContext, - JSONUtils.parseObject(taskExecutionContext.getTaskParams(), new TypeReference() { - })); - this.processInstanceExecCacheManager = processInstanceExecCacheManager; - this.processInstanceDao = processInstanceDao; - this.taskInstanceDao = taskInstanceDao; - } - - @Override - public void handle() throws MasterTaskExecuteException { - DependResult conditionResult = calculateConditionResult(); - DependResult expected = taskParameters.getBlockingOpportunity() - .equals(BlockingOpportunity.BLOCKING_ON_SUCCESS.getDesc()) - ? DependResult.SUCCESS - : DependResult.FAILED; - boolean isBlocked = (expected == conditionResult); - log.info("blocking opportunity: expected-->{}, actual-->{}", expected, conditionResult); - ProcessInstance workflowInstance = processInstanceExecCacheManager - .getByProcessInstanceId(taskExecutionContext.getProcessInstanceId()).getWorkflowExecuteContext() - .getWorkflowInstance(); - workflowInstance.setBlocked(isBlocked); - if (isBlocked) { - workflowInstance.setStateWithDesc(WorkflowExecutionStatus.READY_BLOCK, "ready block"); - } - taskExecutionContext.setCurrentExecutionStatus(TaskExecutionStatus.SUCCESS); - } - - private DependResult calculateConditionResult() throws MasterTaskExecuteException { - // todo: Directly get the task instance from the cache - Map completeTaskList = taskInstanceDao - .queryValidTaskListByWorkflowInstanceId(taskExecutionContext.getProcessInstanceId(), - taskExecutionContext.getTestFlag()) - .stream() - .collect(Collectors.toMap(TaskInstance::getTaskCode, Function.identity())); - - // todo: we need to parse the task parameter from TaskExecutionContext - TaskInstance taskInstance = - processInstanceExecCacheManager.getByProcessInstanceId(taskExecutionContext.getProcessInstanceId()) - .getTaskInstance(taskExecutionContext.getTaskInstanceId()) - .orElseThrow(() -> new MasterTaskExecuteException("Task instance not found")); - DependentParameters dependentParameters = taskInstance.getDependency(); - - List tempResultList = new ArrayList<>(); - for (DependentTaskModel dependentTaskModel : dependentParameters.getDependTaskList()) { - List itemDependResult = new ArrayList<>(); - for (DependentItem item : dependentTaskModel.getDependItemList()) { - itemDependResult.add(getDependResultForItem(item, completeTaskList)); - } - DependResult tempResult = - DependentUtils.getDependResultForRelation(dependentTaskModel.getRelation(), itemDependResult); - tempResultList.add(tempResult); - } - return DependentUtils.getDependResultForRelation(dependentParameters.getRelation(), tempResultList); - } - - private DependResult getDependResultForItem(DependentItem item, Map completeTaskList) { - - DependResult dependResult = DependResult.SUCCESS; - if (!completeTaskList.containsKey(item.getDepTaskCode())) { - log.info("depend item: {} have not completed yet.", item.getDepTaskCode()); - dependResult = DependResult.FAILED; - return dependResult; - } - TaskInstance taskInstance = completeTaskList.get(item.getDepTaskCode()); - if (taskInstance.getState() != item.getStatus()) { - log.info("depend item : {} expect status: {}, actual status: {}", item.getDepTaskCode(), item.getStatus(), - taskInstance.getState().name()); - dependResult = DependResult.FAILED; - } - log.info("Dependent item complete {} {},{}", - Constants.DEPENDENT_SPLIT, item.getDepTaskCode(), dependResult); - return dependResult; - } - -} diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/blocking/BlockingLogicTaskPluginFactory.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/blocking/BlockingLogicTaskPluginFactory.java deleted file mode 100644 index b4fdd56c10..0000000000 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/blocking/BlockingLogicTaskPluginFactory.java +++ /dev/null @@ -1,51 +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.dolphinscheduler.server.master.runner.task.blocking; - -import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; -import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; -import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; -import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; -import org.apache.dolphinscheduler.server.master.runner.task.ILogicTaskPluginFactory; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class BlockingLogicTaskPluginFactory implements ILogicTaskPluginFactory { - - @Autowired - private ProcessInstanceDao processInstanceDao; - - @Autowired - private TaskInstanceDao taskInstanceDao; - - @Autowired - private ProcessInstanceExecCacheManager processInstanceExecCacheManager; - - @Override - public BlockingLogicTask createLogicTask(TaskExecutionContext taskExecutionContext) { - return new BlockingLogicTask(taskExecutionContext, processInstanceExecCacheManager, processInstanceDao, - taskInstanceDao); - } - - @Override - public String getTaskType() { - return BlockingLogicTask.TASK_TYPE; - } -} diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/condition/ConditionLogicTask.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/condition/ConditionLogicTask.java index 803a8043ff..10f7c52cf5 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/condition/ConditionLogicTask.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/condition/ConditionLogicTask.java @@ -17,18 +17,17 @@ package org.apache.dolphinscheduler.server.master.runner.task.condition; -import org.apache.dolphinscheduler.dao.entity.ProcessInstance; +import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.enums.DependResult; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; import org.apache.dolphinscheduler.plugin.task.api.model.DependentItem; -import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameters; +import org.apache.dolphinscheduler.plugin.task.api.parameters.ConditionsParameters; import org.apache.dolphinscheduler.plugin.task.api.utils.DependentUtils; -import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; import org.apache.dolphinscheduler.server.master.exception.LogicTaskInitializeException; +import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; import org.apache.dolphinscheduler.server.master.runner.task.BaseSyncLogicTask; import java.util.List; @@ -39,50 +38,42 @@ import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; +import com.fasterxml.jackson.core.type.TypeReference; + @Slf4j -public class ConditionLogicTask extends BaseSyncLogicTask { +public class ConditionLogicTask extends BaseSyncLogicTask { public static final String TASK_TYPE = "CONDITIONS"; private final TaskInstanceDao taskInstanceDao; - private final ProcessInstanceDao workflowInstanceDao; - public ConditionLogicTask(TaskExecutionContext taskExecutionContext, - ProcessInstanceExecCacheManager processInstanceExecCacheManager, - TaskInstanceDao taskInstanceDao, - ProcessInstanceDao workflowInstanceDao) throws LogicTaskInitializeException { - // todo: we need to change the parameter in front-end, so that we can directly use json to parse - super(taskExecutionContext, - processInstanceExecCacheManager.getByProcessInstanceId(taskExecutionContext.getProcessInstanceId()) - .getTaskInstance(taskExecutionContext.getTaskInstanceId()) - .orElseThrow(() -> new LogicTaskInitializeException( - "Cannot find the task instance in workflow execute runnable")) - .getDependency()); - // todo:check the parameters, why we don't use conditionTask? taskInstance.getDependency(); + public ConditionLogicTask(WorkflowExecuteRunnable workflowExecuteRunnable, + TaskExecutionContext taskExecutionContext, + TaskInstanceDao taskInstanceDao) throws LogicTaskInitializeException { + super(workflowExecuteRunnable, taskExecutionContext, + JSONUtils.parseObject(taskExecutionContext.getTaskParams(), new TypeReference() { + })); this.taskInstanceDao = taskInstanceDao; - this.workflowInstanceDao = workflowInstanceDao; } @Override public void handle() { - // calculate the conditionResult DependResult conditionResult = calculateConditionResult(); - TaskExecutionStatus taskExecutionStatus = - (conditionResult == DependResult.SUCCESS) ? TaskExecutionStatus.SUCCESS : TaskExecutionStatus.FAILURE; - log.info("The condition result is {}, task instance statue will be: {}", conditionResult, taskExecutionStatus); - taskExecutionContext.setCurrentExecutionStatus(taskExecutionStatus); + log.info("The condition result is {}", conditionResult); + taskParameters.getConditionResult().setConditionSuccess(conditionResult == DependResult.SUCCESS); + taskInstance.setTaskParams(JSONUtils.toJsonString(taskParameters)); + taskExecutionContext.setCurrentExecutionStatus(TaskExecutionStatus.SUCCESS); } private DependResult calculateConditionResult() { - final ProcessInstance processInstance = - workflowInstanceDao.queryById(taskExecutionContext.getProcessInstanceId()); - final List taskInstances = - taskInstanceDao.queryValidTaskListByWorkflowInstanceId(processInstance.getId(), - processInstance.getTestFlag()); - final Map taskInstanceMap = - taskInstances.stream().collect(Collectors.toMap(TaskInstance::getTaskCode, Function.identity())); + final List taskInstances = taskInstanceDao.queryValidTaskListByWorkflowInstanceId( + taskExecutionContext.getProcessInstanceId(), taskExecutionContext.getTestFlag()); + final Map taskInstanceMap = taskInstances.stream() + .collect(Collectors.toMap(TaskInstance::getTaskCode, Function.identity())); - List dependResults = taskParameters.getDependTaskList().stream() + ConditionsParameters.ConditionDependency dependence = taskParameters.getDependence(); + List dependResults = dependence.getDependTaskList() + .stream() .map(dependentTaskModel -> DependentUtils.getDependResultForRelation( dependentTaskModel.getRelation(), dependentTaskModel.getDependItemList() @@ -90,7 +81,7 @@ public class ConditionLogicTask extends BaseSyncLogicTask { .map(dependentItem -> getDependResultForItem(dependentItem, taskInstanceMap)) .collect(Collectors.toList()))) .collect(Collectors.toList()); - return DependentUtils.getDependResultForRelation(taskParameters.getRelation(), dependResults); + return DependentUtils.getDependResultForRelation(dependence.getRelation(), dependResults); } private DependResult getDependResultForItem(DependentItem item, Map taskInstanceMap) { @@ -101,8 +92,9 @@ public class ConditionLogicTask extends BaseSyncLogicTask { return DependResult.FAILED; } - DependResult dependResult = - Objects.equals(item.getStatus(), taskInstance.getState()) ? DependResult.SUCCESS : DependResult.FAILED; + DependResult dependResult = Objects.equals(item.getStatus(), taskInstance.getState()) + ? DependResult.SUCCESS + : DependResult.FAILED; log.info("The depend item: {}", item); log.info("Expect status: {}", item.getStatus()); log.info("Actual status: {}", taskInstance.getState()); diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/condition/ConditionLogicTaskPluginFactory.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/condition/ConditionLogicTaskPluginFactory.java index d6887df6b5..f090b6d290 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/condition/ConditionLogicTaskPluginFactory.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/condition/ConditionLogicTaskPluginFactory.java @@ -17,11 +17,12 @@ package org.apache.dolphinscheduler.server.master.runner.task.condition; -import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; +import org.apache.dolphinscheduler.plugin.task.api.task.ConditionsLogicTaskChannelFactory; import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; import org.apache.dolphinscheduler.server.master.exception.LogicTaskInitializeException; +import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; import org.apache.dolphinscheduler.server.master.runner.task.ILogicTaskPluginFactory; import lombok.extern.slf4j.Slf4j; @@ -35,20 +36,19 @@ public class ConditionLogicTaskPluginFactory implements ILogicTaskPluginFactory< @Autowired private TaskInstanceDao taskInstanceDao; - @Autowired - private ProcessInstanceDao processInstanceDao; @Autowired private ProcessInstanceExecCacheManager processInstanceExecCacheManager; @Override public ConditionLogicTask createLogicTask(TaskExecutionContext taskExecutionContext) throws LogicTaskInitializeException { - return new ConditionLogicTask(taskExecutionContext, processInstanceExecCacheManager, taskInstanceDao, - processInstanceDao); + WorkflowExecuteRunnable workflowExecuteRunnable = processInstanceExecCacheManager.getByProcessInstanceId( + taskExecutionContext.getProcessInstanceId()); + return new ConditionLogicTask(workflowExecuteRunnable, taskExecutionContext, taskInstanceDao); } @Override public String getTaskType() { - return ConditionLogicTask.TASK_TYPE; + return ConditionsLogicTaskChannelFactory.NAME; } } diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentAsyncTaskExecuteFunction.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentAsyncTaskExecuteFunction.java index 57f356e2b5..c3bd085657 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentAsyncTaskExecuteFunction.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentAsyncTaskExecuteFunction.java @@ -122,10 +122,12 @@ public class DependentAsyncTaskExecuteFunction implements AsyncTaskExecuteFuncti private List initializeDependentTaskList() { log.info("Begin to initialize dependent task list"); + List dependTaskList = dependentParameters.getDependence().getDependTaskList(); + final Set projectCodes = new HashSet<>(); final Set processDefinitionCodes = new HashSet<>(); final Set taskDefinitionCodes = new HashSet<>(); - for (DependentTaskModel taskModel : dependentParameters.getDependTaskList()) { + for (DependentTaskModel taskModel : dependTaskList) { for (DependentItem dependentItem : taskModel.getDependItemList()) { projectCodes.add(dependentItem.getProjectCode()); processDefinitionCodes.add(dependentItem.getDefinitionCode()); @@ -142,7 +144,7 @@ public class DependentAsyncTaskExecuteFunction implements AsyncTaskExecuteFuncti .collect(Collectors.toMap(TaskDefinition::getCode, Function.identity())); final TaskInstance taskInstance = taskInstanceDao.queryById(taskExecutionContext.getTaskInstanceId()); - List dependentExecutes = dependentParameters.getDependTaskList() + List dependentExecutes = dependTaskList .stream() .map(dependentTaskModel -> { for (DependentItem dependentItem : dependentTaskModel.getDependItemList()) { @@ -210,7 +212,7 @@ public class DependentAsyncTaskExecuteFunction implements AsyncTaskExecuteFuncti } dependResultList.add(dependResult); } - return DependentUtils.getDependResultForRelation(this.dependentParameters.getRelation(), + return DependentUtils.getDependResultForRelation(dependentParameters.getDependence().getRelation(), dependResultList); } @@ -218,7 +220,8 @@ public class DependentAsyncTaskExecuteFunction implements AsyncTaskExecuteFuncti boolean isAllDependentTaskFinished = true; for (DependentExecute dependentExecute : dependentTaskList) { if (!dependentExecute.finish(dependentDate, processInstance.getTestFlag(), - dependentParameters.getFailurePolicy(), dependentParameters.getFailureWaitingTime())) { + dependentParameters.getDependence().getFailurePolicy(), + dependentParameters.getDependence().getFailureWaitingTime())) { isAllDependentTaskFinished = false; } dependentExecute.getDependResultMap().forEach((dependentKey, dependResult) -> { @@ -238,7 +241,7 @@ public class DependentAsyncTaskExecuteFunction implements AsyncTaskExecuteFuncti @Override public @NonNull Duration getAsyncTaskStateCheckInterval() { - return dependentParameters.getCheckInterval() == null ? DEFAULT_STATE_CHECK_INTERVAL - : Duration.ofSeconds(dependentParameters.getCheckInterval()); + return dependentParameters.getDependence().getCheckInterval() == null ? DEFAULT_STATE_CHECK_INTERVAL + : Duration.ofSeconds(dependentParameters.getDependence().getCheckInterval()); } } diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentLogicTask.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentLogicTask.java index e6ee3a78ca..774c6cb1f1 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentLogicTask.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentLogicTask.java @@ -17,6 +17,7 @@ package org.apache.dolphinscheduler.server.master.runner.task.dependent; +import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.dao.repository.ProcessDefinitionDao; import org.apache.dolphinscheduler.dao.repository.ProcessInstanceDao; @@ -26,8 +27,6 @@ import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameters; -import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; -import org.apache.dolphinscheduler.server.master.exception.LogicTaskInitializeException; import org.apache.dolphinscheduler.server.master.exception.MasterTaskExecuteException; import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; import org.apache.dolphinscheduler.server.master.runner.execute.AsyncTaskExecuteFunction; @@ -37,6 +36,8 @@ import java.util.Date; import lombok.extern.slf4j.Slf4j; +import com.fasterxml.jackson.core.type.TypeReference; + @Slf4j public class DependentLogicTask extends BaseAsyncLogicTask { @@ -48,7 +49,7 @@ public class DependentLogicTask extends BaseAsyncLogicTask private final TaskInstanceDao taskInstanceDao; private final ProcessInstanceDao processInstanceDao; - private final ProcessInstanceExecCacheManager processInstanceExecCacheManager; + private final WorkflowExecuteRunnable workflowExecuteRunnable; public DependentLogicTask(TaskExecutionContext taskExecutionContext, ProjectDao projectDao, @@ -56,19 +57,16 @@ public class DependentLogicTask extends BaseAsyncLogicTask TaskDefinitionDao taskDefinitionDao, TaskInstanceDao taskInstanceDao, ProcessInstanceDao processInstanceDao, - ProcessInstanceExecCacheManager processInstanceExecCacheManager) throws LogicTaskInitializeException { + WorkflowExecuteRunnable workflowExecuteRunnable) { super(taskExecutionContext, - processInstanceExecCacheManager.getByProcessInstanceId(taskExecutionContext.getProcessInstanceId()) - .getTaskInstance(taskExecutionContext.getTaskInstanceId()) - .orElseThrow(() -> new LogicTaskInitializeException( - "Cannot find the task instance in workflow execute runnable")) - .getDependency()); + JSONUtils.parseObject(taskExecutionContext.getTaskParams(), new TypeReference() { + })); this.projectDao = projectDao; this.processDefinitionDao = processDefinitionDao; this.taskDefinitionDao = taskDefinitionDao; this.taskInstanceDao = taskInstanceDao; this.processInstanceDao = processInstanceDao; - this.processInstanceExecCacheManager = processInstanceExecCacheManager; + this.workflowExecuteRunnable = workflowExecuteRunnable; } @@ -85,8 +83,6 @@ public class DependentLogicTask extends BaseAsyncLogicTask @Override public void pause() throws MasterTaskExecuteException { - WorkflowExecuteRunnable workflowExecuteRunnable = - processInstanceExecCacheManager.getByProcessInstanceId(taskExecutionContext.getProcessInstanceId()); if (workflowExecuteRunnable == null) { log.error("Cannot find the WorkflowExecuteRunnable"); return; diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentLogicTaskPluginFactory.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentLogicTaskPluginFactory.java index e8012ac3f9..970dd964d1 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentLogicTaskPluginFactory.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/dependent/DependentLogicTaskPluginFactory.java @@ -25,6 +25,7 @@ import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; import org.apache.dolphinscheduler.server.master.exception.LogicTaskInitializeException; +import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; import org.apache.dolphinscheduler.server.master.runner.task.ILogicTaskPluginFactory; import lombok.extern.slf4j.Slf4j; @@ -52,6 +53,12 @@ public class DependentLogicTaskPluginFactory implements ILogicTaskPluginFactory< @Override public DependentLogicTask createLogicTask(TaskExecutionContext taskExecutionContext) throws LogicTaskInitializeException { + int workflowInstanceId = taskExecutionContext.getProcessInstanceId(); + WorkflowExecuteRunnable workflowExecuteRunnable = + processInstanceExecCacheManager.getByProcessInstanceId(workflowInstanceId); + if (workflowExecuteRunnable == null) { + throw new LogicTaskInitializeException("Cannot find the WorkflowExecuteRunnable: " + workflowInstanceId); + } return new DependentLogicTask( taskExecutionContext, projectDao, @@ -59,7 +66,7 @@ public class DependentLogicTaskPluginFactory implements ILogicTaskPluginFactory< taskDefinitionDao, taskInstanceDao, processInstanceDao, - processInstanceExecCacheManager); + workflowExecuteRunnable); } @Override diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/subworkflow/SubWorkflowLogicTask.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/subworkflow/SubWorkflowLogicTask.java index 1883a27d8b..5a44966bca 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/subworkflow/SubWorkflowLogicTask.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/subworkflow/SubWorkflowLogicTask.java @@ -27,7 +27,6 @@ import org.apache.dolphinscheduler.extract.master.ITaskInstanceExecutionEventLis import org.apache.dolphinscheduler.extract.master.transportor.WorkflowInstanceStateChangeEvent; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.SubProcessParameters; -import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; import org.apache.dolphinscheduler.server.master.exception.MasterTaskExecuteException; import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; import org.apache.dolphinscheduler.server.master.runner.execute.AsyncTaskExecuteFunction; @@ -41,29 +40,27 @@ import com.fasterxml.jackson.core.type.TypeReference; public class SubWorkflowLogicTask extends BaseAsyncLogicTask { public static final String TASK_TYPE = "SUB_PROCESS"; - private final ProcessInstanceExecCacheManager processInstanceExecCacheManager; + private final WorkflowExecuteRunnable workflowExecuteRunnable; private final ProcessInstanceDao processInstanceDao; public SubWorkflowLogicTask(TaskExecutionContext taskExecutionContext, - ProcessInstanceExecCacheManager processInstanceExecCacheManager, + WorkflowExecuteRunnable workflowExecuteRunnable, ProcessInstanceDao processInstanceDao) { super(taskExecutionContext, JSONUtils.parseObject(taskExecutionContext.getTaskParams(), new TypeReference() { })); - this.processInstanceExecCacheManager = processInstanceExecCacheManager; + this.workflowExecuteRunnable = workflowExecuteRunnable; this.processInstanceDao = processInstanceDao; } @Override - public AsyncTaskExecuteFunction getAsyncTaskExecuteFunction() throws MasterTaskExecuteException { + public AsyncTaskExecuteFunction getAsyncTaskExecuteFunction() { // todo: create sub workflow instance here? return new SubWorkflowAsyncTaskExecuteFunction(taskExecutionContext, processInstanceDao); } @Override public void pause() throws MasterTaskExecuteException { - WorkflowExecuteRunnable workflowExecuteRunnable = - processInstanceExecCacheManager.getByProcessInstanceId(taskExecutionContext.getProcessInstanceId()); if (workflowExecuteRunnable == null) { log.warn("Cannot find WorkflowExecuteRunnable"); return; @@ -99,8 +96,6 @@ public class SubWorkflowLogicTask extends BaseAsyncLogicTask { @@ -47,104 +52,96 @@ public class SwitchLogicTask extends BaseSyncLogicTask { private final WorkflowExecuteRunnable workflowExecuteRunnable; private final TaskInstance taskInstance; - public SwitchLogicTask(TaskExecutionContext taskExecutionContext, - ProcessInstanceExecCacheManager processInstanceExecCacheManager) throws LogicTaskInitializeException { - super(taskExecutionContext, - // todo: we need to refactor the logic task parameter........ - processInstanceExecCacheManager.getByProcessInstanceId(taskExecutionContext.getProcessInstanceId()) - .getTaskInstance(taskExecutionContext.getTaskInstanceId()) - .orElseThrow(() -> new LogicTaskInitializeException( - "Cannot find the task instance in workflow execute runnable")) - .getSwitchDependency()); - this.workflowExecuteRunnable = - processInstanceExecCacheManager.getByProcessInstanceId(taskExecutionContext.getProcessInstanceId()); - this.taskInstance = workflowExecuteRunnable.getTaskInstance(taskExecutionContext.getTaskInstanceId()) - .orElseThrow(() -> new LogicTaskInitializeException( - "Cannot find the task instance in workflow execute runnable")); + public SwitchLogicTask(WorkflowExecuteRunnable workflowExecuteRunnable, + TaskExecutionContext taskExecutionContext) throws LogicTaskInitializeException { + super(workflowExecuteRunnable, + taskExecutionContext, + JSONUtils.parseObject(taskExecutionContext.getTaskParams(), new TypeReference() { + })); + this.workflowExecuteRunnable = workflowExecuteRunnable; + this.taskInstance = + workflowExecuteRunnable.getTaskInstance(taskExecutionContext.getTaskInstanceId()).orElseThrow( + () -> new LogicTaskInitializeException( + "Cannot find the task instance in workflow execute runnable")); } @Override public void handle() throws MasterTaskExecuteException { - // Calculate the condition result and get the next node - if (CollectionUtils.isEmpty(taskParameters.getDependTaskList())) { + if (CollectionUtils.isEmpty(taskParameters.getSwitchResult().getDependTaskList())) { + // If the branch is empty then will go into the default branch + // This case shouldn't happen, we can directly throw exception and forbid the user to set branch + log.info("The switch items is empty"); moveToDefaultBranch(); } else { calculateSwitchBranch(); } - taskInstance.setSwitchDependency(taskParameters); - log.info("Switch task execute finished"); + checkIfBranchExist(taskParameters.getNextBranch()); + taskInstance.setTaskParams(JSONUtils.toJsonString(taskParameters)); taskExecutionContext.setCurrentExecutionStatus(TaskExecutionStatus.SUCCESS); + log.info("Switch task execute finished: {}", taskExecutionContext.getCurrentExecutionStatus().name()); } private void moveToDefaultBranch() { - checkIfBranchExist(taskParameters.getNextNode()); - - List switchResultVos = taskParameters.getDependTaskList(); - switchResultVos.add(new SwitchResultVo(null, taskParameters.getNextNode())); - taskParameters.setResultConditionLocation(switchResultVos.size() - 1); - + log.info("Begin to move to the default branch"); + if (taskParameters.getSwitchResult().getNextNode() == null) { + throw new IllegalArgumentException( + "The default branch is empty, please check the switch task configuration"); + } + taskParameters.setNextBranch(taskParameters.getSwitchResult().getNextNode()); log.info("The condition is not satisfied, move to the default branch: {}", - taskParameters.getNextNode().stream().map(node -> workflowExecuteRunnable.getWorkflowExecuteContext() - .getWorkflowGraph().getDag().getNode(node).getName()).collect(Collectors.toList())); + getTaskName(taskParameters.getNextBranch())); } private void calculateSwitchBranch() { - List switchResultVos = taskParameters.getDependTaskList(); - if (CollectionUtils.isEmpty(switchResultVos)) { - moveToDefaultBranch(); - } + List switchResultVos = taskParameters.getSwitchResult().getDependTaskList(); Map globalParams = taskExecutionContext.getPrepareParamsMap(); Map varParams = JSONUtils .toList(taskInstance.getVarPool(), Property.class) .stream() .collect(Collectors.toMap(Property::getProp, Property -> Property)); - int finalConditionLocation = -1; - for (int i = 0; i < switchResultVos.size(); i++) { - SwitchResultVo switchResultVo = switchResultVos.get(i); - log.info("Begin to execute {} condition: {} ", i, switchResultVo.getCondition()); - String content = SwitchTaskUtils.generateContentWithTaskParams(switchResultVo.getCondition(), globalParams, - varParams); - log.info("Format condition sentence::{} successfully", content); - boolean result; + Long nextBranch = null; + for (SwitchResultVo switchResultVo : switchResultVos) { + log.info("Begin to execute switch item: {} ", switchResultVo); try { - result = SwitchTaskUtils.evaluate(content); - log.info("Execute condition sentence: {} successfully: {}", content, result); - if (result) { - finalConditionLocation = i; + String content = SwitchTaskUtils.generateContentWithTaskParams(switchResultVo.getCondition(), + globalParams, varParams); + log.info("Format condition sentence::{} successfully", content); + boolean conditionResult = SwitchTaskUtils.evaluate(content); + log.info("Execute condition sentence: {} successfully: {}", content, conditionResult); + if (conditionResult) { + // If matched, break the loop + nextBranch = switchResultVo.getNextNode(); + break; } } catch (Exception e) { - log.info("Execute condition sentence: {} failed", content, e); + log.info("Execute switch item: {} failed", switchResultVo, e); } } - if (finalConditionLocation >= 0) { - checkIfBranchExist(switchResultVos.get(finalConditionLocation).getNextNode()); - log.info("The condition is satisfied, move to the branch: {}", - switchResultVos.get(finalConditionLocation).getNextNode().stream() - .map(node -> workflowExecuteRunnable.getWorkflowExecuteContext().getWorkflowGraph().getDag() - .getNode(node).getName()) - .collect(Collectors.toList())); - taskParameters.setResultConditionLocation(finalConditionLocation); - } else { - log.info("All conditions are not satisfied, move to the default branch"); + + if (nextBranch == null) { + log.info("All switch item is not satisfied"); moveToDefaultBranch(); } } - private void checkIfBranchExist(List branchNode) { - if (CollectionUtils.isEmpty(branchNode)) { - throw new IllegalArgumentException("The branchNode is empty, please check the switch task configuration"); + private void checkIfBranchExist(Long branchNode) { + if (branchNode == null) { + throw new IllegalArgumentException("The branch is empty, please check the switch task configuration"); } - for (Long branch : branchNode) { - if (branch == null) { - throw new IllegalArgumentException("The branch is empty, please check the switch task configuration"); - } - if (!workflowExecuteRunnable.getWorkflowExecuteContext().getWorkflowGraph().getDag().containsNode(branch)) { - throw new IllegalArgumentException( - "The branch(code= " + branchNode - + ") is not in the dag, please check the switch task configuration"); - } + if (!workflowExecuteRunnable.getWorkflowExecuteContext().getWorkflowGraph().getDag().containsNode(branchNode)) { + throw new IllegalArgumentException( + "The branch(code= " + branchNode + + ") is not in the dag, please check the switch task configuration"); } } + private String getTaskName(Long taskCode) { + Optional taskNode = Optional.ofNullable(workflowExecuteRunnable.getWorkflowExecuteContext()) + .map(IWorkflowExecuteContext::getWorkflowGraph) + .map(IWorkflowGraph::getDag) + .map(dag -> dag.getNode(taskCode)); + return taskNode.map(TaskNode::getName).orElse(null); + } + } diff --git a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/switchtask/SwitchLogicTaskPluginFactory.java b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/switchtask/SwitchLogicTaskPluginFactory.java index dfb47aa92d..926e70c637 100644 --- a/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/switchtask/SwitchLogicTaskPluginFactory.java +++ b/dolphinscheduler-master/src/main/java/org/apache/dolphinscheduler/server/master/runner/task/switchtask/SwitchLogicTaskPluginFactory.java @@ -20,6 +20,7 @@ package org.apache.dolphinscheduler.server.master.runner.task.switchtask; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; import org.apache.dolphinscheduler.server.master.exception.LogicTaskInitializeException; +import org.apache.dolphinscheduler.server.master.runner.WorkflowExecuteRunnable; import org.apache.dolphinscheduler.server.master.runner.task.ILogicTaskPluginFactory; import lombok.extern.slf4j.Slf4j; @@ -36,7 +37,14 @@ public class SwitchLogicTaskPluginFactory implements ILogicTaskPluginFactory MASTER_TASK_TYPES = Sets.newHashSet( - BlockingLogicTask.TASK_TYPE, - ConditionLogicTask.TASK_TYPE, - DependentLogicTask.TASK_TYPE, - SubWorkflowLogicTask.TASK_TYPE, - SwitchLogicTask.TASK_TYPE, - DynamicLogicTask.TASK_TYPE); - - public boolean isMasterTask(String taskType) { - return MASTER_TASK_TYPES.contains(taskType); - } -} diff --git a/dolphinscheduler-master/src/main/resources/application.yaml b/dolphinscheduler-master/src/main/resources/application.yaml index 17b1e41a71..da1e986928 100644 --- a/dolphinscheduler-master/src/main/resources/application.yaml +++ b/dolphinscheduler-master/src/main/resources/application.yaml @@ -51,6 +51,7 @@ spring: org.quartz.scheduler.makeSchedulerThreadDaemon: true org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate org.quartz.jobStore.clusterCheckinInterval: 5000 + cloud.discovery.client.composite-indicator.enabled: false # Mybatis-plus configuration, you don't need to change it mybatis-plus: @@ -73,12 +74,12 @@ registry: namespace: dolphinscheduler connect-string: localhost:2181 retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms + base-sleep-time: 1s + max-sleep: 3s max-retries: 5 - session-timeout: 30s - connection-timeout: 9s - block-until-connected: 600ms + session-timeout: 60s + connection-timeout: 15s + block-until-connected: 15s digest: ~ master: diff --git a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteRunnableTest.java b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteRunnableTest.java index 409f1b7691..0adc938d5c 100644 --- a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteRunnableTest.java +++ b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/WorkflowExecuteRunnableTest.java @@ -392,7 +392,6 @@ public class WorkflowExecuteRunnableTest { // task instance already finished, not dispatch TaskInstance taskInstance = new TaskInstance(); taskInstance.setState(TaskExecutionStatus.PAUSE); - Mockito.when(processInstance.isBlocked()).thenReturn(true); TaskExecuteRunnable taskExecuteRunnable = Mockito.mock(TaskExecuteRunnable.class); workflowExecuteThread.tryToDispatchTaskInstance(taskInstance, taskExecuteRunnable); Mockito.verify(taskExecuteRunnable, Mockito.never()).dispatch(); diff --git a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/dispatcher/TaskDispatchFactoryTest.java b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/dispatcher/TaskDispatchFactoryTest.java index 93c8465362..dd63abcfa9 100644 --- a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/dispatcher/TaskDispatchFactoryTest.java +++ b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/dispatcher/TaskDispatchFactoryTest.java @@ -17,15 +17,18 @@ package org.apache.dolphinscheduler.server.master.runner.dispatcher; -import org.apache.dolphinscheduler.server.master.runner.task.blocking.BlockingLogicTask; -import org.apache.dolphinscheduler.server.master.runner.task.condition.ConditionLogicTask; -import org.apache.dolphinscheduler.server.master.runner.task.dependent.DependentLogicTask; -import org.apache.dolphinscheduler.server.master.runner.task.subworkflow.SubWorkflowLogicTask; -import org.apache.dolphinscheduler.server.master.runner.task.switchtask.SwitchLogicTask; +import static com.google.common.truth.Truth.assertThat; + +import org.apache.dolphinscheduler.plugin.task.api.task.ConditionsLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.DependentLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.DynamicLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.SubWorkflowLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.SwitchLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.shell.ShellTaskChannelFactory; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -42,18 +45,20 @@ public class TaskDispatchFactoryTest { @Mock private WorkerTaskDispatcher workerTaskDispatcher; - @Test - public void getTaskDispatcher() { - Assertions.assertEquals(masterTaskDispatcher, - taskDispatchFactory.getTaskDispatcher(BlockingLogicTask.TASK_TYPE)); - Assertions.assertEquals(masterTaskDispatcher, - taskDispatchFactory.getTaskDispatcher(ConditionLogicTask.TASK_TYPE)); - Assertions.assertEquals(masterTaskDispatcher, - taskDispatchFactory.getTaskDispatcher(DependentLogicTask.TASK_TYPE)); - Assertions.assertEquals(masterTaskDispatcher, - taskDispatchFactory.getTaskDispatcher(SubWorkflowLogicTask.TASK_TYPE)); - Assertions.assertEquals(masterTaskDispatcher, taskDispatchFactory.getTaskDispatcher(SwitchLogicTask.TASK_TYPE)); + @ParameterizedTest + @ValueSource(strings = { + ConditionsLogicTaskChannelFactory.NAME, + DependentLogicTaskChannelFactory.NAME, + DynamicLogicTaskChannelFactory.NAME, + SubWorkflowLogicTaskChannelFactory.NAME, + SwitchLogicTaskChannelFactory.NAME}) + public void getTaskDispatcher_withLogicTask(String taskType) { + assertThat(taskDispatchFactory.getTaskDispatcher(taskType)).isSameInstanceAs(masterTaskDispatcher); + } - Assertions.assertEquals(workerTaskDispatcher, taskDispatchFactory.getTaskDispatcher("SHELL")); + @ParameterizedTest + @ValueSource(strings = {ShellTaskChannelFactory.NAME}) + public void getTaskDispatcher_withWorkerTask(String taskType) { + assertThat(taskDispatchFactory.getTaskDispatcher(taskType)).isSameInstanceAs(workerTaskDispatcher); } } diff --git a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/queue/DelayEntryTest.java b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/queue/DelayEntryTest.java index 0e3ab5da36..00cf782e18 100644 --- a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/queue/DelayEntryTest.java +++ b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/queue/DelayEntryTest.java @@ -17,19 +17,19 @@ package org.apache.dolphinscheduler.server.master.runner.queue; +import static com.google.common.truth.Truth.assertThat; + import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; -import com.google.common.truth.Truth; - class DelayEntryTest { @Test void getDelay() { - DelayEntry delayEntry = new DelayEntry<>(1_000L, "Item"); - Truth.assertThat(delayEntry.getDelay(TimeUnit.NANOSECONDS)) - .isWithin(100) - .of(TimeUnit.NANOSECONDS.convert(1_000L, TimeUnit.MILLISECONDS)); + DelayEntry delayEntry = new DelayEntry<>(5_000L, "Item"); + assertThat(delayEntry.getDelay(TimeUnit.NANOSECONDS)) + .isWithin(TimeUnit.NANOSECONDS.convert(500, TimeUnit.MILLISECONDS)) + .of(TimeUnit.NANOSECONDS.convert(5_000L, TimeUnit.MILLISECONDS)); } } diff --git a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/task/dynamic/DynamicLogicTaskTest.java b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/task/dynamic/DynamicLogicTaskTest.java index a5b9f5a371..019b435fe0 100644 --- a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/task/dynamic/DynamicLogicTaskTest.java +++ b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/runner/task/dynamic/DynamicLogicTaskTest.java @@ -19,6 +19,7 @@ package org.apache.dolphinscheduler.server.master.runner.task.dynamic; import org.apache.dolphinscheduler.common.enums.CommandType; import org.apache.dolphinscheduler.common.enums.WorkflowExecutionStatus; +import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.ProcessInstance; import org.apache.dolphinscheduler.dao.mapper.CommandMapper; import org.apache.dolphinscheduler.dao.mapper.ProcessDefinitionMapper; @@ -43,8 +44,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import com.fasterxml.jackson.databind.ObjectMapper; - @ExtendWith(MockitoExtension.class) class DynamicLogicTaskTest { @@ -74,14 +73,11 @@ class DynamicLogicTaskTest { private DynamicLogicTask dynamicLogicTask; - private ObjectMapper objectMapper; - @BeforeEach public void setUp() { // Set up your test environment before each test. dynamicParameters = new DynamicParameters(); taskExecutionContext = Mockito.mock(TaskExecutionContext.class); - objectMapper = new ObjectMapper(); processInstance = new ProcessInstance(); Mockito.when(processInstanceDao.queryById(Mockito.any())).thenReturn(processInstance); dynamicLogicTask = new DynamicLogicTask( @@ -95,7 +91,7 @@ class DynamicLogicTaskTest { } @Test - void testGenerateParameterGroup() throws Exception { + void testGenerateParameterGroup() { DynamicInputParameter dynamicInputParameter1 = new DynamicInputParameter(); dynamicInputParameter1.setName("param1"); dynamicInputParameter1.setValue("a,b,c"); @@ -113,7 +109,7 @@ class DynamicLogicTaskTest { Mockito.when(taskExecutionContext.getPrepareParamsMap()).thenReturn(new HashMap<>()); Mockito.when(taskExecutionContext.getTaskParams()) - .thenReturn(objectMapper.writeValueAsString(dynamicParameters)); + .thenReturn(JSONUtils.toJsonString(dynamicParameters)); dynamicLogicTask = new DynamicLogicTask( taskExecutionContext, diff --git a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/service/FailoverServiceTest.java b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/service/FailoverServiceTest.java index 2081d2dd7a..7e6f30970f 100644 --- a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/service/FailoverServiceTest.java +++ b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/service/FailoverServiceTest.java @@ -17,8 +17,6 @@ package org.apache.dolphinscheduler.server.master.service; -import static org.apache.dolphinscheduler.common.constants.Constants.COMMON_TASK_TYPE; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SWITCH; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doNothing; @@ -31,6 +29,8 @@ import org.apache.dolphinscheduler.dao.entity.ProcessInstance; import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; +import org.apache.dolphinscheduler.plugin.task.api.task.SwitchLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.shell.ShellTaskChannelFactory; import org.apache.dolphinscheduler.registry.api.RegistryClient; import org.apache.dolphinscheduler.registry.api.enums.RegistryNodeType; import org.apache.dolphinscheduler.server.master.cache.ProcessInstanceExecCacheManager; @@ -139,13 +139,13 @@ public class FailoverServiceTest { masterTaskInstance.setId(1); masterTaskInstance.setStartTime(new Date()); masterTaskInstance.setHost(testMasterHost); - masterTaskInstance.setTaskType(TASK_TYPE_SWITCH); + masterTaskInstance.setTaskType(SwitchLogicTaskChannelFactory.NAME); workerTaskInstance = new TaskInstance(); workerTaskInstance.setId(2); workerTaskInstance.setStartTime(new Date()); workerTaskInstance.setHost(testWorkerHost); - workerTaskInstance.setTaskType(COMMON_TASK_TYPE); + workerTaskInstance.setTaskType(ShellTaskChannelFactory.NAME); given(processService.queryNeedFailoverProcessInstances(Mockito.anyString())) .willReturn(Arrays.asList(processInstance)); diff --git a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/SwitchTaskUtilsTest.java b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/SwitchTaskUtilsTest.java index 7f7ae43bbf..34785ada47 100644 --- a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/SwitchTaskUtilsTest.java +++ b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/SwitchTaskUtilsTest.java @@ -50,7 +50,7 @@ public class SwitchTaskUtilsTest { Map globalParams = new HashMap<>(); Map varParams = new HashMap<>(); globalParams.put("test", new Property("test", Direct.IN, DataType.INTEGER, "1")); - Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> { + Assertions.assertDoesNotThrow(() -> { SwitchTaskUtils.generateContentWithTaskParams(content, globalParams, varParams); }); @@ -70,15 +70,5 @@ public class SwitchTaskUtilsTest { SwitchTaskUtils.evaluate(script); }); - String contentWithSpecify1 = "cmd.abc"; - Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> { - SwitchTaskUtils.generateContentWithTaskParams(contentWithSpecify1, globalParams, varParams); - }); - - String contentWithSpecify2 = "cmd()"; - Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> { - SwitchTaskUtils.generateContentWithTaskParams(contentWithSpecify2, globalParams, varParams); - }); - } } diff --git a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/TaskUtilsTest.java b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/TaskUtilsTest.java deleted file mode 100644 index bec04b1936..0000000000 --- a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/TaskUtilsTest.java +++ /dev/null @@ -1,39 +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.dolphinscheduler.server.master.utils; - -import org.apache.dolphinscheduler.server.master.runner.task.blocking.BlockingLogicTask; -import org.apache.dolphinscheduler.server.master.runner.task.condition.ConditionLogicTask; -import org.apache.dolphinscheduler.server.master.runner.task.dependent.DependentLogicTask; -import org.apache.dolphinscheduler.server.master.runner.task.subworkflow.SubWorkflowLogicTask; -import org.apache.dolphinscheduler.server.master.runner.task.switchtask.SwitchLogicTask; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TaskUtilsTest { - - @Test - public void isMasterTask() { - Assertions.assertTrue(TaskUtils.isMasterTask(BlockingLogicTask.TASK_TYPE)); - Assertions.assertTrue(TaskUtils.isMasterTask(ConditionLogicTask.TASK_TYPE)); - Assertions.assertTrue(TaskUtils.isMasterTask(DependentLogicTask.TASK_TYPE)); - Assertions.assertTrue(TaskUtils.isMasterTask(SubWorkflowLogicTask.TASK_TYPE)); - Assertions.assertTrue(TaskUtils.isMasterTask(SwitchLogicTask.TASK_TYPE)); - } -} diff --git a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/WorkflowInstanceUtilsTest.java b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/WorkflowInstanceUtilsTest.java index d52c436add..5b4bc18ca1 100644 --- a/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/WorkflowInstanceUtilsTest.java +++ b/dolphinscheduler-master/src/test/java/org/apache/dolphinscheduler/server/master/utils/WorkflowInstanceUtilsTest.java @@ -25,6 +25,7 @@ import org.apache.dolphinscheduler.common.enums.WorkflowExecutionStatus; import org.apache.dolphinscheduler.dao.entity.ProcessDefinition; import org.apache.dolphinscheduler.dao.entity.ProcessInstance; import org.apache.dolphinscheduler.dao.entity.TaskInstance; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; import java.sql.Date; @@ -52,7 +53,7 @@ public class WorkflowInstanceUtilsTest { workflowInstance.setDryRun(0); workflowInstance.setTenantCode("default"); workflowInstance.setRestartTime(Date.valueOf("2023-08-01")); - workflowInstance.setWorkerGroup("default"); + workflowInstance.setWorkerGroup(WorkerGroupUtils.getDefaultWorkerGroup()); workflowInstance.setStartTime(Date.valueOf("2023-08-01")); workflowInstance.setEndTime(Date.valueOf("2023-08-01")); Assertions.assertEquals("\n" diff --git a/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-etcd/src/test/java/org/apache/dolphinscheduler/plugin/registry/etcd/EtcdRegistryTestCase.java b/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-etcd/src/test/java/org/apache/dolphinscheduler/plugin/registry/etcd/EtcdRegistryTestCase.java index 1e751c1862..39bfea8cc7 100644 --- a/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-etcd/src/test/java/org/apache/dolphinscheduler/plugin/registry/etcd/EtcdRegistryTestCase.java +++ b/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-etcd/src/test/java/org/apache/dolphinscheduler/plugin/registry/etcd/EtcdRegistryTestCase.java @@ -51,6 +51,7 @@ public class EtcdRegistryTestCase extends RegistryTestCase { .build() .cluster(); etcdCluster.start(); + System.clearProperty("registry.endpoints"); System.setProperty("registry.endpoints", etcdCluster.clientEndpoints().stream().map(URI::toString).collect(Collectors.joining(","))); } diff --git a/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/main/java/org/apache/dolphinscheduler/plugin/registry/jdbc/JdbcOperator.java b/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/main/java/org/apache/dolphinscheduler/plugin/registry/jdbc/JdbcOperator.java index 95f58a4a20..46ba84db8a 100644 --- a/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/main/java/org/apache/dolphinscheduler/plugin/registry/jdbc/JdbcOperator.java +++ b/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/main/java/org/apache/dolphinscheduler/plugin/registry/jdbc/JdbcOperator.java @@ -27,7 +27,6 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import java.sql.SQLException; -import java.sql.SQLIntegrityConstraintViolationException; import java.util.Collection; import java.util.Date; import java.util.List; @@ -142,7 +141,6 @@ public final class JdbcOperator { /** * Try to acquire the target Lock, if cannot acquire, return null. */ - @SuppressWarnings("checkstyle:IllegalCatch") public JdbcRegistryLock tryToAcquireLock(String key) { JdbcRegistryLock jdbcRegistryLock = JdbcRegistryLock.builder() .lockKey(key) @@ -154,7 +152,7 @@ public final class JdbcOperator { jdbcRegistryLockMapper.insert(jdbcRegistryLock); return jdbcRegistryLock; } catch (Exception e) { - if (e instanceof SQLIntegrityConstraintViolationException || e instanceof DuplicateKeyException) { + if (e instanceof DuplicateKeyException) { return null; } throw e; diff --git a/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/test/java/org/apache/dolphinscheduler/plugin/registry/jdbc/JdbcRegistryTestCase.java b/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/test/java/org/apache/dolphinscheduler/plugin/registry/jdbc/JdbcRegistryTestCase.java index 8dbf7a844e..c5c83b62e4 100644 --- a/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/test/java/org/apache/dolphinscheduler/plugin/registry/jdbc/JdbcRegistryTestCase.java +++ b/dolphinscheduler-registry/dolphinscheduler-registry-plugins/dolphinscheduler-registry-jdbc/src/test/java/org/apache/dolphinscheduler/plugin/registry/jdbc/JdbcRegistryTestCase.java @@ -17,8 +17,14 @@ package org.apache.dolphinscheduler.plugin.registry.jdbc; -import org.apache.dolphinscheduler.plugin.registry.RegistryTestCase; +import static com.google.common.truth.Truth.assertThat; +import org.apache.dolphinscheduler.plugin.registry.RegistryTestCase; +import org.apache.dolphinscheduler.plugin.registry.jdbc.model.JdbcRegistryLock; + +import lombok.SneakyThrows; + +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; @@ -33,6 +39,18 @@ public abstract class JdbcRegistryTestCase extends RegistryTestCase { @@ -52,11 +48,11 @@ class ZookeeperRegistryTestCase extends RegistryTestCase { @BeforeAll public static void setUpTestingServer() { zookeeperContainer = new GenericContainer<>(DockerImageName.parse("zookeeper:3.8")) - .withNetwork(NETWORK); - int randomPort = RandomUtils.nextInt(10000, 65535); - zookeeperContainer.setPortBindings(Lists.newArrayList(randomPort + ":2181")); + .withNetwork(NETWORK) + .withExposedPorts(2181); Startables.deepStart(Stream.of(zookeeperContainer)).join(); - System.setProperty("registry.zookeeper.connect-string", "localhost:" + randomPort); + System.clearProperty("registry.zookeeper.connect-string"); + System.setProperty("registry.zookeeper.connect-string", "localhost:" + zookeeperContainer.getMappedPort(2181)); } @SneakyThrows diff --git a/dolphinscheduler-scheduler-plugin/dolphinscheduler-scheduler-quartz/src/main/java/org/apache/dolphinscheduler/scheduler/quartz/ProcessScheduleTask.java b/dolphinscheduler-scheduler-plugin/dolphinscheduler-scheduler-quartz/src/main/java/org/apache/dolphinscheduler/scheduler/quartz/ProcessScheduleTask.java index c6bd4cf93f..8ce6480c45 100644 --- a/dolphinscheduler-scheduler-plugin/dolphinscheduler-scheduler-quartz/src/main/java/org/apache/dolphinscheduler/scheduler/quartz/ProcessScheduleTask.java +++ b/dolphinscheduler-scheduler-plugin/dolphinscheduler-scheduler-quartz/src/main/java/org/apache/dolphinscheduler/scheduler/quartz/ProcessScheduleTask.java @@ -17,17 +17,15 @@ package org.apache.dolphinscheduler.scheduler.quartz; -import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.CommandType; import org.apache.dolphinscheduler.common.enums.ReleaseState; import org.apache.dolphinscheduler.dao.entity.Command; import org.apache.dolphinscheduler.dao.entity.ProcessDefinition; import org.apache.dolphinscheduler.dao.entity.Schedule; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.service.command.CommandService; import org.apache.dolphinscheduler.service.process.ProcessService; -import org.apache.commons.lang3.StringUtils; - import java.util.Date; import lombok.extern.slf4j.Slf4j; @@ -93,8 +91,7 @@ public class ProcessScheduleTask extends QuartzJobBean { command.setScheduleTime(scheduledFireTime); command.setStartTime(fireTime); command.setWarningGroupId(schedule.getWarningGroupId()); - String workerGroup = StringUtils.isEmpty(schedule.getWorkerGroup()) ? Constants.DEFAULT_WORKER_GROUP - : schedule.getWorkerGroup(); + String workerGroup = WorkerGroupUtils.getWorkerGroupOrDefault(schedule.getWorkerGroup()); command.setWorkerGroup(workerGroup); command.setTenantCode(schedule.getTenantCode()); command.setEnvironmentCode(schedule.getEnvironmentCode()); diff --git a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/alert/ListenerEventAlertManager.java b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/alert/ListenerEventAlertManager.java index 182ed678a0..3056a14f5b 100644 --- a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/alert/ListenerEventAlertManager.java +++ b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/alert/ListenerEventAlertManager.java @@ -43,6 +43,7 @@ import org.apache.dolphinscheduler.dao.entity.event.TaskFailListenerEvent; import org.apache.dolphinscheduler.dao.entity.event.TaskStartListenerEvent; import org.apache.dolphinscheduler.dao.mapper.AlertPluginInstanceMapper; import org.apache.dolphinscheduler.dao.mapper.ListenerEventMapper; +import org.apache.dolphinscheduler.service.process.ProcessService; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.collections4.CollectionUtils; @@ -71,6 +72,9 @@ public class ListenerEventAlertManager { @Autowired private AlertPluginInstanceMapper alertPluginInstanceMapper; + @Autowired + private ProcessService processService; + public void publishServerDownListenerEvent(String host, String type) { ServerDownListenerEvent event = new ServerDownListenerEvent(); event.setEventTime(new Date()); @@ -214,8 +218,9 @@ public class ListenerEventAlertManager { } public void publishTaskFailListenerEvent(ProcessInstance processInstance, - TaskInstance taskInstance, - ProjectUser projectUser) { + TaskInstance taskInstance) { + ProjectUser projectUser = processService.queryProjectWithUserByProcessInstanceId(processInstance.getId()); + TaskFailListenerEvent event = new TaskFailListenerEvent(); event.setProjectCode(projectUser.getProjectCode()); event.setProjectName(projectUser.getProjectName()); diff --git a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/model/TaskNode.java b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/model/TaskNode.java index 954003ec41..c9601f15b5 100644 --- a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/model/TaskNode.java +++ b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/model/TaskNode.java @@ -17,27 +17,17 @@ package org.apache.dolphinscheduler.service.model; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_BLOCKING; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_CONDITIONS; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SWITCH; - import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.Priority; import org.apache.dolphinscheduler.common.enums.TaskExecuteType; -import org.apache.dolphinscheduler.common.model.PreviousTaskNode; import org.apache.dolphinscheduler.common.utils.JSONUtils; -import org.apache.dolphinscheduler.plugin.task.api.enums.TaskTimeoutStrategy; -import org.apache.dolphinscheduler.plugin.task.api.parameters.TaskTimeoutParameter; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -118,11 +108,6 @@ public class TaskNode { @JsonSerialize(using = JSONUtils.JsonDataSerializer.class) private String preTasks; - /** - * node dependency list - */ - private List preTaskNodeList; - /** * users store additional information */ @@ -135,25 +120,6 @@ public class TaskNode { */ private List depList; - /** - * outer dependency information - */ - @JsonDeserialize(using = JSONUtils.JsonDataDeserializer.class) - @JsonSerialize(using = JSONUtils.JsonDataSerializer.class) - private String dependence; - - @JsonDeserialize(using = JSONUtils.JsonDataDeserializer.class) - @JsonSerialize(using = JSONUtils.JsonDataSerializer.class) - private String conditionResult; - - @JsonDeserialize(using = JSONUtils.JsonDataDeserializer.class) - @JsonSerialize(using = JSONUtils.JsonDataSerializer.class) - private String switchResult; - - @JsonDeserialize(using = JSONUtils.JsonDataDeserializer.class) - @JsonSerialize(using = JSONUtils.JsonDataSerializer.class) - private String waitStartTimeout; - /** * task instance priority */ @@ -312,10 +278,8 @@ public class TaskNode { && Objects.equals(preTasks, taskNode.preTasks) && Objects.equals(extras, taskNode.extras) && Objects.equals(runFlag, taskNode.runFlag) - && Objects.equals(dependence, taskNode.dependence) && Objects.equals(workerGroup, taskNode.workerGroup) && Objects.equals(environmentCode, taskNode.environmentCode) - && Objects.equals(conditionResult, taskNode.conditionResult) && CollectionUtils.isEqualCollection(depList, taskNode.depList) && Objects.equals(taskExecuteType, taskNode.taskExecuteType); } @@ -325,14 +289,6 @@ public class TaskNode { return Objects.hash(name, desc, type, params, preTasks, extras, depList, runFlag); } - public String getDependence() { - return dependence; - } - - public void setDependence(String dependence) { - this.dependence = dependence; - } - public int getMaxRetryTimes() { return maxRetryTimes; } @@ -373,14 +329,6 @@ public class TaskNode { this.workerGroup = workerGroup; } - public String getConditionResult() { - return conditionResult; - } - - public void setConditionResult(String conditionResult) { - this.conditionResult = conditionResult; - } - public int getDelayTime() { return delayTime; } @@ -405,64 +353,6 @@ public class TaskNode { this.version = version; } - /** - * get task time out parameter - * - * @return task time out parameter - */ - public TaskTimeoutParameter getTaskTimeoutParameter() { - if (!StringUtils.isEmpty(this.getTimeout())) { - String formatStr = - String.format("%s,%s", TaskTimeoutStrategy.WARN.name(), TaskTimeoutStrategy.FAILED.name()); - String taskTimeout = this.getTimeout().replace(formatStr, TaskTimeoutStrategy.WARNFAILED.name()); - return JSONUtils.parseObject(taskTimeout, TaskTimeoutParameter.class); - } - return new TaskTimeoutParameter(false); - } - - public boolean isConditionsTask() { - return TASK_TYPE_CONDITIONS.equalsIgnoreCase(this.getType()); - } - - public boolean isSwitchTask() { - return TASK_TYPE_SWITCH.equalsIgnoreCase(this.getType()); - } - - public List getPreTaskNodeList() { - return preTaskNodeList; - } - - public boolean isBlockingTask() { - return TASK_TYPE_BLOCKING.equalsIgnoreCase(this.getType()); - } - - public void setPreTaskNodeList(List preTaskNodeList) { - this.preTaskNodeList = preTaskNodeList; - } - - public String getTaskParams() { - Map taskParams = JSONUtils.parseObject(this.params, new TypeReference>() { - }); - - if (taskParams == null) { - taskParams = new HashMap<>(); - } - taskParams.put(Constants.CONDITION_RESULT, this.conditionResult); - taskParams.put(Constants.DEPENDENCE, this.dependence); - taskParams.put(Constants.SWITCH_RESULT, this.switchResult); - taskParams.put(Constants.WAIT_START_TIMEOUT, this.waitStartTimeout); - return JSONUtils.toJsonString(taskParams); - } - - public Map taskParamsToJsonObj(String taskParams) { - Map taskParamsMap = JSONUtils.parseObject(taskParams, new TypeReference>() { - }); - if (taskParamsMap == null) { - taskParamsMap = new HashMap<>(); - } - return taskParamsMap; - } - @Override public String toString() { return "TaskNode{" @@ -478,11 +368,8 @@ public class TaskNode { + ", retryInterval=" + retryInterval + ", params='" + params + '\'' + ", preTasks='" + preTasks + '\'' - + ", preTaskNodeList=" + preTaskNodeList + ", extras='" + extras + '\'' + ", depList=" + depList - + ", dependence='" + dependence + '\'' - + ", conditionResult='" + conditionResult + '\'' + ", taskInstancePriority=" + taskInstancePriority + ", workerGroup='" + workerGroup + '\'' + ", environmentCode=" + environmentCode @@ -500,22 +387,6 @@ public class TaskNode { return this.environmentCode; } - public String getSwitchResult() { - return switchResult; - } - - public void setSwitchResult(String switchResult) { - this.switchResult = switchResult; - } - - public String getWaitStartTimeout() { - return this.waitStartTimeout; - } - - public void setWaitStartTimeout(String waitStartTimeout) { - this.waitStartTimeout = waitStartTimeout; - } - public int getTaskGroupId() { return taskGroupId; } diff --git a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/process/ProcessService.java b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/process/ProcessService.java index 8787aabc8b..9a5692b4c0 100644 --- a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/process/ProcessService.java +++ b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/process/ProcessService.java @@ -176,8 +176,6 @@ public interface ProcessService { DqComparisonType getComparisonTypeById(int id); - void changeTaskGroupQueueStatus(int taskId, TaskGroupQueueStatus status); - TaskGroupQueue insertIntoTaskGroupQueue(Integer taskId, String taskName, Integer groupId, diff --git a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/process/ProcessServiceImpl.java b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/process/ProcessServiceImpl.java index 635948fc86..cfc1fc3a8c 100644 --- a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/process/ProcessServiceImpl.java +++ b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/process/ProcessServiceImpl.java @@ -107,6 +107,8 @@ import org.apache.dolphinscheduler.dao.repository.TaskDefinitionDao; import org.apache.dolphinscheduler.dao.repository.TaskDefinitionLogDao; import org.apache.dolphinscheduler.dao.repository.TaskInstanceDao; import org.apache.dolphinscheduler.dao.utils.DqRuleUtils; +import org.apache.dolphinscheduler.dao.utils.EnvironmentUtils; +import org.apache.dolphinscheduler.dao.utils.WorkerGroupUtils; import org.apache.dolphinscheduler.extract.base.client.SingletonJdkDynamicRpcClientProxyFactory; import org.apache.dolphinscheduler.extract.common.ILogService; import org.apache.dolphinscheduler.extract.master.ITaskInstanceExecutionEventListener; @@ -118,6 +120,7 @@ import org.apache.dolphinscheduler.plugin.task.api.model.Property; import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo; import org.apache.dolphinscheduler.plugin.task.api.parameters.SubProcessParameters; import org.apache.dolphinscheduler.plugin.task.api.parameters.TaskTimeoutParameter; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import org.apache.dolphinscheduler.service.command.CommandService; import org.apache.dolphinscheduler.service.cron.CronUtils; import org.apache.dolphinscheduler.service.exceptions.CronParseException; @@ -573,10 +576,8 @@ public class ProcessServiceImpl implements ProcessService { // set process instance priority processInstance.setProcessInstancePriority(command.getProcessInstancePriority()); - String workerGroup = StringUtils.defaultIfEmpty(command.getWorkerGroup(), Constants.DEFAULT_WORKER_GROUP); - processInstance.setWorkerGroup(workerGroup); - processInstance - .setEnvironmentCode(Objects.isNull(command.getEnvironmentCode()) ? -1 : command.getEnvironmentCode()); + processInstance.setWorkerGroup(WorkerGroupUtils.getWorkerGroupOrDefault(command.getWorkerGroup())); + processInstance.setEnvironmentCode(EnvironmentUtils.getEnvironmentCodeOrDefault(command.getEnvironmentCode())); processInstance.setTimeout(processDefinition.getTimeout()); processInstance.setTenantCode(command.getTenantCode()); return processInstance; @@ -1051,7 +1052,7 @@ public class ProcessServiceImpl implements ProcessService { */ private void initTaskInstance(TaskInstance taskInstance) { - if (!taskInstance.isSubProcess() + if (!TaskTypeUtils.isSubWorkflowTask(taskInstance.getTaskType()) && (taskInstance.getState().isKill() || taskInstance.getState().isFailure())) { taskInstance.setFlag(Flag.NO); taskInstanceDao.updateById(taskInstance); @@ -1169,7 +1170,7 @@ public class ProcessServiceImpl implements ProcessService { */ @Override public void createSubWorkProcess(ProcessInstance parentProcessInstance, TaskInstance task) { - if (!task.isSubProcess()) { + if (!TaskTypeUtils.isSubWorkflowTask(task.getTaskType())) { return; } // check create sub work flow firstly @@ -1940,13 +1941,7 @@ public class ProcessServiceImpl implements ProcessService { : Constants.FLOWNODE_RUN_FLAG_FORBIDDEN); taskNode.setMaxRetryTimes(taskDefinitionLog.getFailRetryTimes()); taskNode.setRetryInterval(taskDefinitionLog.getFailRetryInterval()); - Map taskParamsMap = taskNode.taskParamsToJsonObj(taskDefinitionLog.getTaskParams()); - taskNode.setConditionResult(JSONUtils.toJsonString(taskParamsMap.get(Constants.CONDITION_RESULT))); - taskNode.setSwitchResult(JSONUtils.toJsonString(taskParamsMap.get(Constants.SWITCH_RESULT))); - taskNode.setDependence(JSONUtils.toJsonString(taskParamsMap.get(Constants.DEPENDENCE))); - taskParamsMap.remove(Constants.CONDITION_RESULT); - taskParamsMap.remove(Constants.DEPENDENCE); - taskNode.setParams(JSONUtils.toJsonString(taskParamsMap)); + taskNode.setParams(taskDefinitionLog.getTaskParams()); taskNode.setTaskInstancePriority(taskDefinitionLog.getTaskPriority()); taskNode.setWorkerGroup(taskDefinitionLog.getWorkerGroup()); taskNode.setEnvironmentCode(taskDefinitionLog.getEnvironmentCode()); @@ -2039,22 +2034,6 @@ public class ProcessServiceImpl implements ProcessService { return dqComparisonTypeMapper.selectById(id); } - /** - * release the TGQ resource when the corresponding task is finished. - * - * @param taskId task id - * @return the result code and msg - */ - - @Override - public void changeTaskGroupQueueStatus(int taskId, TaskGroupQueueStatus status) { - TaskGroupQueue taskGroupQueue = taskGroupQueueMapper.queryByTaskId(taskId); - taskGroupQueue.setInQueue(Flag.NO.getCode()); - taskGroupQueue.setStatus(status); - taskGroupQueue.setUpdateTime(new Date(System.currentTimeMillis())); - taskGroupQueueMapper.updateById(taskGroupQueue); - } - @Override public TaskGroupQueue insertIntoTaskGroupQueue(Integer taskInstanceId, String taskName, diff --git a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/utils/DagHelper.java b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/utils/DagHelper.java index ee5e97cf55..7e6cbda608 100644 --- a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/utils/DagHelper.java +++ b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/utils/DagHelper.java @@ -23,10 +23,12 @@ import org.apache.dolphinscheduler.common.model.TaskNodeRelation; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.ProcessTaskRelation; import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.plugin.task.api.TaskConstants; import org.apache.dolphinscheduler.plugin.task.api.model.SwitchResultVo; import org.apache.dolphinscheduler.plugin.task.api.parameters.ConditionsParameters; import org.apache.dolphinscheduler.plugin.task.api.parameters.SwitchParameters; +import org.apache.dolphinscheduler.plugin.task.api.task.ConditionsLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.SwitchLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.utils.TaskTypeUtils; import org.apache.dolphinscheduler.service.model.TaskNode; import org.apache.dolphinscheduler.service.process.ProcessDag; @@ -35,6 +37,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -44,6 +47,9 @@ import java.util.Set; import lombok.extern.slf4j.Slf4j; +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.collect.Lists; + /** * dag tools */ @@ -76,10 +82,10 @@ public class DagHelper { /** * generate task nodes needed by dag * - * @param taskNodeList taskNodeList - * @param startNodeNameList startNodeNameList + * @param taskNodeList taskNodeList + * @param startNodeNameList startNodeNameList * @param recoveryNodeCodeList recoveryNodeCodeList - * @param taskDependType taskDependType + * @param taskDependType taskDependType * @return task node list */ public static List generateFlowNodeListByStartNode(List taskNodeList, @@ -139,7 +145,7 @@ public class DagHelper { /** * find all the nodes that depended on the start node * - * @param startNode startNode + * @param startNode startNode * @param taskNodeList taskNodeList * @return task node list */ @@ -166,9 +172,9 @@ public class DagHelper { /** * find all nodes that start nodes depend on. * - * @param startNode startNode + * @param startNode startNode * @param recoveryNodeCodeList recoveryNodeCodeList - * @param taskNodeList taskNodeList + * @param taskNodeList taskNodeList * @return task node list */ private static List getFlowNodeListPre(TaskNode startNode, @@ -204,10 +210,10 @@ public class DagHelper { /** * generate dag by start nodes and recovery nodes * - * @param totalTaskNodeList totalTaskNodeList - * @param startNodeNameList startNodeNameList + * @param totalTaskNodeList totalTaskNodeList + * @param startNodeNameList startNodeNameList * @param recoveryNodeCodeList recoveryNodeCodeList - * @param depNodeType depNodeType + * @param depNodeType depNodeType * @return process dag * @throws Exception if error throws Exception */ @@ -228,27 +234,11 @@ public class DagHelper { return processDag; } - /** - * find node by node name - * - * @param nodeDetails nodeDetails - * @param nodeName nodeName - * @return task node - */ - public static TaskNode findNodeByName(List nodeDetails, String nodeName) { - for (TaskNode taskNode : nodeDetails) { - if (taskNode.getName().equals(nodeName)) { - return taskNode; - } - } - return null; - } - /** * find node by node code * * @param nodeDetails nodeDetails - * @param nodeCode nodeCode + * @param nodeCode nodeCode * @return task node */ public static TaskNode findNodeByCode(List nodeDetails, Long nodeCode) { @@ -263,8 +253,8 @@ public class DagHelper { /** * the task can be submit when all the depends nodes are forbidden or complete * - * @param taskNode taskNode - * @param dag dag + * @param taskNode taskNode + * @param dag dag * @param completeTaskList completeTaskList * @return can submit */ @@ -305,10 +295,10 @@ public class DagHelper { if (preNodeCode == null) { startVertexes = dag.getBeginNode(); - } else if (dag.getNode(preNodeCode).isConditionsTask()) { + } else if (TaskTypeUtils.isConditionTask(dag.getNode(preNodeCode).getType())) { List conditionTaskList = parseConditionTask(preNodeCode, skipTaskNodeList, dag, completeTaskList); startVertexes.addAll(conditionTaskList); - } else if (dag.getNode(preNodeCode).isSwitchTask()) { + } else if (TaskTypeUtils.isSwitchTask(dag.getNode(preNodeCode).getType())) { List conditionTaskList = parseSwitchTask(preNodeCode, skipTaskNodeList, dag, completeTaskList); startVertexes.addAll(conditionTaskList); } else { @@ -321,7 +311,7 @@ public class DagHelper { continue; } if (isTaskNodeNeedSkip(taskNode, skipTaskNodeList)) { - setTaskNodeSkip(subsequent, dag, completeTaskList, skipTaskNodeList); + setTaskNodeSkip(subsequent, dag, skipTaskNodeList); continue; } if (!DagHelper.allDependsForbiddenOrEnd(taskNode, dag, skipTaskNodeList, completeTaskList)) { @@ -362,7 +352,7 @@ public class DagHelper { Map completeTaskList) { List conditionTaskList = new ArrayList<>(); TaskNode taskNode = dag.getNode(nodeCode); - if (!taskNode.isConditionsTask()) { + if (!TaskTypeUtils.isConditionTask(taskNode.getType())) { return conditionTaskList; } if (!completeTaskList.containsKey(nodeCode)) { @@ -370,21 +360,21 @@ public class DagHelper { } TaskInstance taskInstance = completeTaskList.get(nodeCode); ConditionsParameters conditionsParameters = - JSONUtils.parseObject(taskNode.getConditionResult(), ConditionsParameters.class); + JSONUtils.parseObject(taskInstance.getTaskParams(), new TypeReference() { + }); + ConditionsParameters.ConditionResult conditionResult = conditionsParameters.getConditionResult(); + List skipNodeList = new ArrayList<>(); - if (taskInstance.getState().isSuccess()) { - conditionTaskList = conditionsParameters.getSuccessNode(); - skipNodeList = conditionsParameters.getFailedNode(); - } else if (taskInstance.getState().isFailure()) { - conditionTaskList = conditionsParameters.getFailedNode(); - skipNodeList = conditionsParameters.getSuccessNode(); + if (conditionResult.isConditionSuccess()) { + conditionTaskList = conditionResult.getSuccessNode(); + skipNodeList = conditionResult.getFailedNode(); } else { - conditionTaskList.add(nodeCode); + conditionTaskList = conditionResult.getFailedNode(); + skipNodeList = conditionResult.getSuccessNode(); } - // the skipNodeList maybe null if no next task - skipNodeList = Optional.ofNullable(skipNodeList).orElse(new ArrayList<>()); - for (Long failedNode : skipNodeList) { - setTaskNodeSkip(failedNode, dag, completeTaskList, skipTaskNodeList); + + if (CollectionUtils.isNotEmpty(skipNodeList)) { + skipNodeList.forEach(skipNode -> setTaskNodeSkip(skipNode, dag, skipTaskNodeList)); } // the conditionTaskList maybe null if no next task conditionTaskList = Optional.ofNullable(conditionTaskList).orElse(new ArrayList<>()); @@ -404,88 +394,57 @@ public class DagHelper { Map completeTaskList) { List conditionTaskList = new ArrayList<>(); TaskNode taskNode = dag.getNode(nodeCode); - if (!taskNode.isSwitchTask()) { + if (!SwitchLogicTaskChannelFactory.NAME.equals(taskNode.getType())) { return conditionTaskList; } if (!completeTaskList.containsKey(nodeCode)) { return conditionTaskList; } - conditionTaskList = skipTaskNode4Switch(taskNode, skipTaskNodeList, completeTaskList, dag); + conditionTaskList = skipTaskNode4Switch(skipTaskNodeList, completeTaskList.get(nodeCode), dag); return conditionTaskList; } - public static List skipTaskNode4Switch(TaskNode taskNode, - Map skipTaskNodeList, - Map completeTaskList, + public static List skipTaskNode4Switch(Map skipTaskNodeList, + TaskInstance taskInstance, DAG dag) { - SwitchParameters switchParameters = - completeTaskList.get(taskNode.getCode()).getSwitchDependency(); - int resultConditionLocation = switchParameters.getResultConditionLocation(); - List conditionResultVoList = switchParameters.getDependTaskList(); - List switchTaskList = conditionResultVoList.get(resultConditionLocation).getNextNode(); - Set switchNeedWorkCodes = new HashSet<>(); - if (CollectionUtils.isEmpty(switchTaskList)) { - return new ArrayList<>(); + JSONUtils.parseObject(taskInstance.getTaskParams(), new TypeReference() { + }); + + SwitchParameters.SwitchResult switchResult = switchParameters.getSwitchResult(); + Long nextBranch = switchParameters.getNextBranch(); + if (switchResult == null) { + log.error("switchResult is null, please check the switch task configuration"); + return Collections.emptyList(); } - // get all downstream nodes of the branch that the switch node needs to execute - for (Long switchTaskCode : switchTaskList) { - getSwitchNeedWorkCodes(switchTaskCode, dag, switchNeedWorkCodes); + if (nextBranch == null) { + log.error("switchParameters.getNextBranch() is null, please check the switch task configuration"); + return Collections.emptyList(); } - // conditionResultVoList.remove(resultConditionLocation); - for (SwitchResultVo info : conditionResultVoList) { - if (CollectionUtils.isEmpty(info.getNextNode())) { - continue; - } - for (Long nextNode : info.getNextNode()) { - setSwitchTaskNodeSkip(nextNode, dag, completeTaskList, skipTaskNodeList, - switchNeedWorkCodes); + + Set allNextBranches = new HashSet<>(); + if (switchResult.getNextNode() != null) { + allNextBranches.add(switchResult.getNextNode()); + } + if (CollectionUtils.isNotEmpty(switchResult.getDependTaskList())) { + for (SwitchResultVo switchResultVo : switchResult.getDependTaskList()) { + allNextBranches.add(switchResultVo.getNextNode()); } } - return switchTaskList; + + allNextBranches.remove(nextBranch); + + for (Long branch : allNextBranches) { + setTaskNodeSkip(branch, dag, skipTaskNodeList); + } + return Lists.newArrayList(nextBranch); } - /** - * get all downstream nodes of the branch that the switch node needs to execute - * @param taskCode - * @param dag - * @param switchNeedWorkCodes - */ - public static void getSwitchNeedWorkCodes(Long taskCode, DAG dag, - Set switchNeedWorkCodes) { - switchNeedWorkCodes.add(taskCode); - Set subsequentNodes = dag.getSubsequentNodes(taskCode); - if (org.apache.commons.collections.CollectionUtils.isNotEmpty(subsequentNodes)) { - for (Long subCode : subsequentNodes) { - getSwitchNeedWorkCodes(subCode, dag, switchNeedWorkCodes); - } - } - } - - private static void setSwitchTaskNodeSkip(Long skipNodeCode, - DAG dag, - Map completeTaskList, - Map skipTaskNodeList, - Set switchNeedWorkCodes) { - // ignore when the node that needs to be skipped exists on the branch that the switch type node needs to execute - if (!dag.containsNode(skipNodeCode) || switchNeedWorkCodes.contains(skipNodeCode)) { - return; - } - skipTaskNodeList.putIfAbsent(skipNodeCode, dag.getNode(skipNodeCode)); - Collection postNodeList = dag.getSubsequentNodes(skipNodeCode); - for (Long post : postNodeList) { - TaskNode postNode = dag.getNode(post); - if (isTaskNodeNeedSkip(postNode, skipTaskNodeList)) { - setTaskNodeSkip(post, dag, completeTaskList, skipTaskNodeList); - } - } - } /** * set task node and the post nodes skip flag */ private static void setTaskNodeSkip(Long skipNodeCode, DAG dag, - Map completeTaskList, Map skipTaskNodeList) { if (!dag.containsNode(skipNodeCode)) { return; @@ -495,7 +454,7 @@ public class DagHelper { for (Long post : postNodeList) { TaskNode postNode = dag.getNode(post); if (isTaskNodeNeedSkip(postNode, skipTaskNodeList)) { - setTaskNodeSkip(post, dag, completeTaskList, skipTaskNodeList); + setTaskNodeSkip(post, dag, skipTaskNodeList); } } } @@ -591,31 +550,7 @@ public class DagHelper { */ public static boolean haveConditionsAfterNode(Long parentNodeCode, DAG dag) { - return haveSubAfterNode(parentNodeCode, dag, TaskConstants.TASK_TYPE_CONDITIONS); - } - - /** - * is there have conditions after the parent node - */ - public static boolean haveConditionsAfterNode(Long parentNodeCode, List taskNodes) { - if (CollectionUtils.isEmpty(taskNodes)) { - return false; - } - for (TaskNode taskNode : taskNodes) { - List preTasksList = JSONUtils.toList(taskNode.getPreTasks(), Long.class); - if (preTasksList.contains(parentNodeCode) && taskNode.isConditionsTask()) { - return true; - } - } - return false; - } - - /** - * is there have blocking node after the parent node - */ - public static boolean haveBlockingAfterNode(Long parentNodeCode, - DAG dag) { - return haveSubAfterNode(parentNodeCode, dag, TaskConstants.TASK_TYPE_BLOCKING); + return haveSubAfterNode(parentNodeCode, dag, ConditionsLogicTaskChannelFactory.NAME); } /** diff --git a/dolphinscheduler-service/src/test/java/org/apache/dolphinscheduler/service/alert/ListenerEventAlertManagerTest.java b/dolphinscheduler-service/src/test/java/org/apache/dolphinscheduler/service/alert/ListenerEventAlertManagerTest.java index 981b0a8050..4ba958e71d 100644 --- a/dolphinscheduler-service/src/test/java/org/apache/dolphinscheduler/service/alert/ListenerEventAlertManagerTest.java +++ b/dolphinscheduler-service/src/test/java/org/apache/dolphinscheduler/service/alert/ListenerEventAlertManagerTest.java @@ -18,6 +18,7 @@ package org.apache.dolphinscheduler.service.alert; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; import org.apache.dolphinscheduler.dao.entity.AlertPluginInstance; import org.apache.dolphinscheduler.dao.entity.ProcessDefinition; @@ -30,6 +31,7 @@ import org.apache.dolphinscheduler.dao.entity.TaskInstance; import org.apache.dolphinscheduler.dao.entity.User; import org.apache.dolphinscheduler.dao.mapper.AlertPluginInstanceMapper; import org.apache.dolphinscheduler.dao.mapper.ListenerEventMapper; +import org.apache.dolphinscheduler.service.process.ProcessService; import java.util.ArrayList; import java.util.List; @@ -40,8 +42,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * ProcessAlertManager Test @@ -49,8 +49,6 @@ import org.slf4j.LoggerFactory; @ExtendWith(MockitoExtension.class) public class ListenerEventAlertManagerTest { - private static final Logger logger = LoggerFactory.getLogger(ListenerEventAlertManagerTest.class); - @InjectMocks ListenerEventAlertManager listenerEventAlertManager; @@ -60,6 +58,9 @@ public class ListenerEventAlertManagerTest { @Mock ListenerEventMapper listenerEventMapper; + @Mock + ProcessService processService; + @Test public void sendServerDownListenerEventTest() { String host = "127.0.0.1"; @@ -67,7 +68,7 @@ public class ListenerEventAlertManagerTest { List globalPluginInstanceList = new ArrayList<>(); AlertPluginInstance instance = new AlertPluginInstance(1, "instanceParams", "instanceName"); globalPluginInstanceList.add(instance); - Mockito.when(alertPluginInstanceMapper.queryAllGlobalAlertPluginInstanceList()) + when(alertPluginInstanceMapper.queryAllGlobalAlertPluginInstanceList()) .thenReturn(globalPluginInstanceList); Mockito.doNothing().when(listenerEventMapper).insertServerDownEvent(any(), any()); listenerEventAlertManager.publishServerDownListenerEvent(host, type); @@ -82,9 +83,9 @@ public class ListenerEventAlertManagerTest { AlertPluginInstance instance = new AlertPluginInstance(1, "instanceParams", "instanceName"); List globalPluginInstanceList = new ArrayList<>(); globalPluginInstanceList.add(instance); - Mockito.when(alertPluginInstanceMapper.queryAllGlobalAlertPluginInstanceList()) + when(alertPluginInstanceMapper.queryAllGlobalAlertPluginInstanceList()) .thenReturn(globalPluginInstanceList); - Mockito.when(listenerEventMapper.insert(any())).thenReturn(1); + when(listenerEventMapper.insert(any())).thenReturn(1); listenerEventAlertManager.publishProcessDefinitionCreatedListenerEvent(user, processDefinition, taskDefinitionLogs, processTaskRelationLogs); } @@ -142,7 +143,8 @@ public class ListenerEventAlertManagerTest { public void sendTaskFailListenerEvent() { ProcessInstance processInstance = Mockito.mock(ProcessInstance.class); TaskInstance taskInstance = Mockito.mock(TaskInstance.class); - ProjectUser projectUser = Mockito.mock(ProjectUser.class); - listenerEventAlertManager.publishTaskFailListenerEvent(processInstance, taskInstance, projectUser); + when(processService.queryProjectWithUserByProcessInstanceId(processInstance.getId())) + .thenReturn(new ProjectUser()); + listenerEventAlertManager.publishTaskFailListenerEvent(processInstance, taskInstance); } } diff --git a/dolphinscheduler-service/src/test/java/org/apache/dolphinscheduler/service/utils/DagHelperTest.java b/dolphinscheduler-service/src/test/java/org/apache/dolphinscheduler/service/utils/DagHelperTest.java index c19812303c..daaa6ac81d 100644 --- a/dolphinscheduler-service/src/test/java/org/apache/dolphinscheduler/service/utils/DagHelperTest.java +++ b/dolphinscheduler-service/src/test/java/org/apache/dolphinscheduler/service/utils/DagHelperTest.java @@ -17,18 +17,19 @@ package org.apache.dolphinscheduler.service.utils; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_CONDITIONS; - import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.TaskDependType; import org.apache.dolphinscheduler.common.graph.DAG; import org.apache.dolphinscheduler.common.model.TaskNodeRelation; import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.dao.entity.TaskInstance; -import org.apache.dolphinscheduler.plugin.task.api.TaskConstants; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; import org.apache.dolphinscheduler.plugin.task.api.model.SwitchResultVo; +import org.apache.dolphinscheduler.plugin.task.api.parameters.ConditionsParameters; import org.apache.dolphinscheduler.plugin.task.api.parameters.SwitchParameters; +import org.apache.dolphinscheduler.plugin.task.api.task.ConditionsLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.DependentLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.SwitchLogicTaskChannelFactory; import org.apache.dolphinscheduler.service.model.TaskNode; import org.apache.dolphinscheduler.service.process.ProcessDag; @@ -43,10 +44,9 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.collect.Lists; +import com.google.common.truth.Truth; -/** - * dag helper test - */ public class DagHelperTest { @Test @@ -89,26 +89,105 @@ public class DagHelperTest { boolean canSubmit = DagHelper.haveAllNodeAfterNode(parentNodeCode, dag); Assertions.assertTrue(canSubmit); - boolean haveBlocking = DagHelper.haveBlockingAfterNode(parentNodeCode, dag); - Assertions.assertTrue(haveBlocking); - boolean haveConditions = DagHelper.haveConditionsAfterNode(parentNodeCode, dag); Assertions.assertTrue(haveConditions); - boolean dependent = DagHelper.haveSubAfterNode(parentNodeCode, dag, TaskConstants.TASK_TYPE_DEPENDENT); + boolean dependent = DagHelper.haveSubAfterNode(parentNodeCode, dag, DependentLogicTaskChannelFactory.NAME); Assertions.assertFalse(dependent); } - /** - * test task node can submit - * - * @throws JsonProcessingException if error throws JsonProcessingException - */ @Test - public void testTaskNodeCanSubmit() throws IOException { + public void testTaskNodeCanSubmit() { + List taskNodeList = new ArrayList<>(); + TaskNode node1 = new TaskNode(); + node1.setId("1"); + node1.setName("1"); + node1.setCode(1); + node1.setType("SHELL"); + taskNodeList.add(node1); + + TaskNode node2 = new TaskNode(); + node2.setId("2"); + node2.setName("2"); + node2.setCode(2); + node2.setType("SHELL"); + List dep2 = new ArrayList<>(); + dep2.add("1"); + node2.setPreTasks(JSONUtils.toJsonString(dep2)); + taskNodeList.add(node2); + + TaskNode node4 = new TaskNode(); + node4.setId("4"); + node4.setName("4"); + node4.setCode(4); + node4.setType("SHELL"); + taskNodeList.add(node4); + + TaskNode node3 = new TaskNode(); + node3.setId("3"); + node3.setName("3"); + node3.setCode(3); + node3.setType("SHELL"); + List dep3 = new ArrayList<>(); + dep3.add("2"); + dep3.add("4"); + node3.setPreTasks(JSONUtils.toJsonString(dep3)); + taskNodeList.add(node3); + + TaskNode node5 = new TaskNode(); + node5.setId("5"); + node5.setName("5"); + node5.setCode(5); + node5.setType("SHELL"); + List dep5 = new ArrayList<>(); + dep5.add("3"); + dep5.add("8"); + node5.setPreTasks(JSONUtils.toJsonString(dep5)); + taskNodeList.add(node5); + + TaskNode node6 = new TaskNode(); + node6.setId("6"); + node6.setName("6"); + node6.setCode(6); + node6.setType("SHELL"); + List dep6 = new ArrayList<>(); + dep6.add("3"); + node6.setPreTasks(JSONUtils.toJsonString(dep6)); + taskNodeList.add(node6); + + TaskNode node7 = new TaskNode(); + node7.setId("7"); + node7.setName("7"); + node7.setCode(7); + node7.setType("SHELL"); + List dep7 = new ArrayList<>(); + dep7.add("5"); + node7.setPreTasks(JSONUtils.toJsonString(dep7)); + taskNodeList.add(node7); + + TaskNode node8 = new TaskNode(); + node8.setId("8"); + node8.setName("8"); + node8.setCode(8); + node8.setType("SHELL"); + List dep8 = new ArrayList<>(); + dep8.add("2"); + node8.setPreTasks(JSONUtils.toJsonString(dep8)); + taskNodeList.add(node8); + + List startNodes = new ArrayList<>(); + List recoveryNodes = new ArrayList<>(); + List destTaskNodeList = DagHelper.generateFlowNodeListByStartNode(taskNodeList, + startNodes, recoveryNodes, TaskDependType.TASK_POST); + List taskNodeRelations = DagHelper.generateRelationListByFlowNodes(destTaskNodeList); + ProcessDag processDag = new ProcessDag(); + processDag.setEdges(taskNodeRelations); + processDag.setNodes(destTaskNodeList); + // 1->2->3->5->7 // 4->3->6 - DAG dag = generateDag(); + // 1->2->8->5->7 + DAG dag = DagHelper.buildDagGraph(processDag); TaskNode taskNode3 = dag.getNode(3L); Map completeTaskList = new HashMap<>(); Map skipNodeList = new HashMap<>(); @@ -116,7 +195,7 @@ public class DagHelperTest { Boolean canSubmit = false; // 2/4 are forbidden submit 3 - TaskNode node2 = dag.getNode(2L); + node2 = dag.getNode(2L); node2.setRunFlag(Constants.FLOWNODE_RUN_FLAG_FORBIDDEN); TaskNode nodex = dag.getNode(4L); nodex.setRunFlag(Constants.FLOWNODE_RUN_FLAG_FORBIDDEN); @@ -131,21 +210,107 @@ public class DagHelperTest { Assertions.assertEquals(canSubmit, false); // 2/3 forbidden submit 5 - TaskNode node3 = dag.getNode(3L); + node3 = dag.getNode(3L); node3.setRunFlag(Constants.FLOWNODE_RUN_FLAG_FORBIDDEN); - TaskNode node8 = dag.getNode(8L); + node8 = dag.getNode(8L); node8.setRunFlag(Constants.FLOWNODE_RUN_FLAG_FORBIDDEN); - TaskNode node5 = dag.getNode(5L); + node5 = dag.getNode(5L); canSubmit = DagHelper.allDependsForbiddenOrEnd(node5, dag, skipNodeList, completeTaskList); Assertions.assertEquals(canSubmit, true); } - /** - * test parse post node list - */ @Test - public void testParsePostNodeList() throws IOException { - DAG dag = generateDag(); + public void testParsePostNodeList() { + List taskNodeList = new ArrayList<>(); + TaskNode node1 = new TaskNode(); + node1.setId("1"); + node1.setName("1"); + node1.setCode(1); + node1.setType("SHELL"); + taskNodeList.add(node1); + + TaskNode node2 = new TaskNode(); + node2.setId("2"); + node2.setName("2"); + node2.setCode(2); + node2.setType("SHELL"); + List dep2 = new ArrayList<>(); + dep2.add("1"); + node2.setPreTasks(JSONUtils.toJsonString(dep2)); + taskNodeList.add(node2); + + TaskNode node4 = new TaskNode(); + node4.setId("4"); + node4.setName("4"); + node4.setCode(4); + node4.setType("SHELL"); + taskNodeList.add(node4); + + TaskNode node3 = new TaskNode(); + node3.setId("3"); + node3.setName("3"); + node3.setCode(3); + node3.setType("SHELL"); + List dep3 = new ArrayList<>(); + dep3.add("2"); + dep3.add("4"); + node3.setPreTasks(JSONUtils.toJsonString(dep3)); + taskNodeList.add(node3); + + TaskNode node5 = new TaskNode(); + node5.setId("5"); + node5.setName("5"); + node5.setCode(5); + node5.setType("SHELL"); + List dep5 = new ArrayList<>(); + dep5.add("3"); + dep5.add("8"); + node5.setPreTasks(JSONUtils.toJsonString(dep5)); + taskNodeList.add(node5); + + TaskNode node6 = new TaskNode(); + node6.setId("6"); + node6.setName("6"); + node6.setCode(6); + node6.setType("SHELL"); + List dep6 = new ArrayList<>(); + dep6.add("3"); + node6.setPreTasks(JSONUtils.toJsonString(dep6)); + taskNodeList.add(node6); + + TaskNode node7 = new TaskNode(); + node7.setId("7"); + node7.setName("7"); + node7.setCode(7); + node7.setType("SHELL"); + List dep7 = new ArrayList<>(); + dep7.add("5"); + node7.setPreTasks(JSONUtils.toJsonString(dep7)); + taskNodeList.add(node7); + + TaskNode node8 = new TaskNode(); + node8.setId("8"); + node8.setName("8"); + node8.setCode(8); + node8.setType("SHELL"); + List dep8 = new ArrayList<>(); + dep8.add("2"); + node8.setPreTasks(JSONUtils.toJsonString(dep8)); + taskNodeList.add(node8); + + List startNodes = new ArrayList<>(); + List recoveryNodes = new ArrayList<>(); + List destTaskNodeList = DagHelper.generateFlowNodeListByStartNode(taskNodeList, + startNodes, recoveryNodes, TaskDependType.TASK_POST); + List taskNodeRelations = DagHelper.generateRelationListByFlowNodes(destTaskNodeList); + ProcessDag processDag = new ProcessDag(); + processDag.setEdges(taskNodeRelations); + processDag.setNodes(destTaskNodeList); + + // 1->2->3->5->7 + // 4->3->6 + // 1->2->8->5->7 + DAG dag = DagHelper.buildDagGraph(processDag); Map completeTaskList = new HashMap<>(); Map skipNodeList = new HashMap<>(); @@ -205,11 +370,6 @@ public class DagHelperTest { Assertions.assertTrue(postNodes.contains(7L)); } - /** - * test forbidden post node - * - * @throws JsonProcessingException - */ @Test public void testForbiddenPostNode() throws IOException { DAG dag = generateDag(); @@ -243,11 +403,6 @@ public class DagHelperTest { Assertions.assertTrue(postNodes.contains(3L)); } - /** - * test condition post node - * - * @throws JsonProcessingException - */ @Test public void testConditionPostNode() throws IOException { DAG dag = generateDag(); @@ -259,24 +414,22 @@ public class DagHelperTest { completeTaskList.put(1L, new TaskInstance()); completeTaskList.put(2L, new TaskInstance()); completeTaskList.put(4L, new TaskInstance()); + + TaskInstance taskInstance3 = new TaskInstance(); + taskInstance3.setTaskType(ConditionsLogicTaskChannelFactory.NAME); + ConditionsParameters.ConditionResult conditionResult = ConditionsParameters.ConditionResult.builder() + .conditionSuccess(true) + .successNode(Lists.newArrayList(5L)) + .failedNode(Lists.newArrayList(6L)) + .build(); + ConditionsParameters conditionsParameters = new ConditionsParameters(); + conditionsParameters.setConditionResult(conditionResult); + taskInstance3.setTaskParams(JSONUtils.toJsonString(conditionsParameters)); + taskInstance3.setState(TaskExecutionStatus.SUCCESS); TaskNode node3 = dag.getNode(3L); - node3.setType(TASK_TYPE_CONDITIONS); - node3.setConditionResult("{\n" - + - " \"successNode\": [5\n" - + - " ],\n" - + - " \"failedNode\": [6\n" - + - " ]\n" - + - " }"); - completeTaskList.remove(3L); - TaskInstance taskInstance = new TaskInstance(); - taskInstance.setState(TaskExecutionStatus.SUCCESS); + node3.setType(ConditionsLogicTaskChannelFactory.NAME); // complete 1/2/3/4 expect:8 - completeTaskList.put(3L, taskInstance); + completeTaskList.put(3L, taskInstance3); postNodes = DagHelper.parsePostNodes(null, skipNodeList, dag, completeTaskList); Assertions.assertEquals(1, postNodes.size()); Assertions.assertTrue(postNodes.contains(8L)); @@ -291,7 +444,6 @@ public class DagHelperTest { // 3.complete 1/2/3/4/5/8 expect post:7 skip:6 skipNodeList.clear(); TaskInstance taskInstance1 = new TaskInstance(); - taskInstance.setState(TaskExecutionStatus.SUCCESS); completeTaskList.put(5L, taskInstance1); postNodes = DagHelper.parsePostNodes(null, skipNodeList, dag, completeTaskList); Assertions.assertEquals(1, postNodes.size()); @@ -299,66 +451,120 @@ public class DagHelperTest { Assertions.assertEquals(1, skipNodeList.size()); Assertions.assertTrue(skipNodeList.containsKey(6L)); - // dag: 1-2-3-5-7 4-3-6 - // 3-if , complete:1/2/3/4 - // 1.failure:3 expect post:6 skip:5/7 - skipNodeList.clear(); - completeTaskList.remove(3L); - taskInstance = new TaskInstance(); - - Map taskParamsMap = new HashMap<>(); - taskParamsMap.put(Constants.SWITCH_RESULT, ""); - taskInstance.setTaskParams(JSONUtils.toJsonString(taskParamsMap)); - taskInstance.setState(TaskExecutionStatus.FAILURE); - completeTaskList.put(3L, taskInstance); - postNodes = DagHelper.parsePostNodes(null, skipNodeList, dag, completeTaskList); - Assertions.assertEquals(1, postNodes.size()); - Assertions.assertTrue(postNodes.contains(6L)); - Assertions.assertEquals(2, skipNodeList.size()); - Assertions.assertTrue(skipNodeList.containsKey(5L)); - Assertions.assertTrue(skipNodeList.containsKey(7L)); - - // dag: 1-2-3-5-7 4-3-6 - // 3-if , complete:1/2/3/4 - // 1.failure:3 expect post:6 skip:5/7 - dag = generateDag2(); - skipNodeList.clear(); - completeTaskList.clear(); - taskInstance.setSwitchDependency(getSwitchNode()); - completeTaskList.put(1L, taskInstance); - postNodes = DagHelper.parsePostNodes(1L, skipNodeList, dag, completeTaskList); - Assertions.assertEquals(1, postNodes.size()); } @Test - public void testSwitchPostNode() throws IOException { - DAG dag = generateDag2(); + public void testSwitchPostNode() { + List taskNodeList = new ArrayList<>(); + + TaskNode node = new TaskNode(); + node.setId("0"); + node.setName("0"); + node.setCode(0); + node.setType("SHELL"); + taskNodeList.add(node); + + TaskNode node1 = new TaskNode(); + node1.setId("1"); + node1.setName("1"); + node1.setCode(1); + node1.setType(SwitchLogicTaskChannelFactory.NAME); + SwitchParameters switchParameters = new SwitchParameters(); + node1.setParams(JSONUtils.toJsonString(switchParameters)); + taskNodeList.add(node1); + + TaskNode node2 = new TaskNode(); + node2.setId("2"); + node2.setName("2"); + node2.setCode(2); + node2.setType("SHELL"); + List dep2 = new ArrayList<>(); + dep2.add("1"); + node2.setPreTasks(JSONUtils.toJsonString(dep2)); + taskNodeList.add(node2); + + TaskNode node4 = new TaskNode(); + node4.setId("4"); + node4.setName("4"); + node4.setCode(4); + node4.setType("SHELL"); + List dep4 = new ArrayList<>(); + dep4.add("1"); + node4.setPreTasks(JSONUtils.toJsonString(dep4)); + taskNodeList.add(node4); + + TaskNode node5 = new TaskNode(); + node5.setId("5"); + node5.setName("5"); + node5.setCode(5); + node5.setType("SHELL"); + List dep5 = new ArrayList<>(); + dep5.add(1L); + node5.setPreTasks(JSONUtils.toJsonString(dep5)); + taskNodeList.add(node5); + + TaskNode node6 = new TaskNode(); + node5.setId("6"); + node5.setName("6"); + node5.setCode(6); + node5.setType("SHELL"); + List dep6 = new ArrayList<>(); + dep5.add(2L); + dep5.add(4L); + node5.setPreTasks(JSONUtils.toJsonString(dep6)); + taskNodeList.add(node6); + + List startNodes = new ArrayList<>(); + List recoveryNodes = new ArrayList<>(); + + // 0 + // 1->2->6 + // 1->4->6 + // 1->5 + List destTaskNodeList = DagHelper.generateFlowNodeListByStartNode(taskNodeList, + startNodes, recoveryNodes, TaskDependType.TASK_POST); + List taskNodeRelations = DagHelper.generateRelationListByFlowNodes(destTaskNodeList); + ProcessDag processDag = new ProcessDag(); + processDag.setEdges(taskNodeRelations); + processDag.setNodes(destTaskNodeList); + + DAG dag = DagHelper.buildDagGraph(processDag); Map skipTaskNodeList = new HashMap<>(); Map completeTaskList = new HashMap<>(); - completeTaskList.put(0l, new TaskInstance()); + completeTaskList.put(0L, new TaskInstance()); TaskInstance taskInstance = new TaskInstance(); taskInstance.setState(TaskExecutionStatus.SUCCESS); - taskInstance.setTaskCode(1l); - Map taskParamsMap = new HashMap<>(); - taskParamsMap.put(Constants.SWITCH_RESULT, ""); - taskInstance.setTaskParams(JSONUtils.toJsonString(taskParamsMap)); - taskInstance.setSwitchDependency(getSwitchNode()); + taskInstance.setTaskCode(1L); + taskInstance.setTaskType(SwitchLogicTaskChannelFactory.NAME); + switchParameters = SwitchParameters.builder() + .nextBranch(5L) + .switchResult(SwitchParameters.SwitchResult.builder() + .dependTaskList(Lists.newArrayList( + new SwitchResultVo("", 2L), + new SwitchResultVo("", 4L))) + .nextNode(5L) + .build()) + .build(); + taskInstance.setTaskParams(JSONUtils.toJsonString(switchParameters)); completeTaskList.put(1l, taskInstance); - DagHelper.skipTaskNode4Switch(dag.getNode(1l), skipTaskNodeList, completeTaskList, dag); + List nextBranch = DagHelper.skipTaskNode4Switch(skipTaskNodeList, taskInstance, dag); Assertions.assertNotNull(skipTaskNodeList.get(2L)); - Assertions.assertEquals(1, skipTaskNodeList.size()); + Assertions.assertNotNull(skipTaskNodeList.get(4L)); + Assertions.assertEquals(2, skipTaskNodeList.size()); + Truth.assertThat(nextBranch).containsExactly(5L); } + /** * process: * 1->2->3->5->7 * 4->3->6 * 1->2->8->5->7 * DAG graph: - * 4 -> -> 6 - * \ / + * 4 -> -> 6 + * \ / * 1 -> 2 -> 3 -> 5 -> 7 - * \ / - * -> 8 -> + * \ / + * -> 8 -> * * @return dag * @throws JsonProcessingException if error throws JsonProcessingException @@ -452,108 +658,6 @@ public class DagHelperTest { return DagHelper.buildDagGraph(processDag); } - /** - * DAG graph: - * -> 2-> - * / \ - * / \ - * 0->1(switch)->5 6 - * \ / - * \ / - * -> 4-> - * - * @return dag - * @throws JsonProcessingException if error throws JsonProcessingException - */ - private DAG generateDag2() throws IOException { - List taskNodeList = new ArrayList<>(); - - TaskNode node = new TaskNode(); - node.setId("0"); - node.setName("0"); - node.setCode(0); - node.setType("SHELL"); - taskNodeList.add(node); - - TaskNode node1 = new TaskNode(); - node1.setId("1"); - node1.setName("1"); - node1.setCode(1); - node1.setType("switch"); - node1.setDependence(JSONUtils.toJsonString(getSwitchNode())); - taskNodeList.add(node1); - - TaskNode node2 = new TaskNode(); - node2.setId("2"); - node2.setName("2"); - node2.setCode(2); - node2.setType("SHELL"); - List dep2 = new ArrayList<>(); - dep2.add("1"); - node2.setPreTasks(JSONUtils.toJsonString(dep2)); - taskNodeList.add(node2); - - TaskNode node4 = new TaskNode(); - node4.setId("4"); - node4.setName("4"); - node4.setCode(4); - node4.setType("SHELL"); - List dep4 = new ArrayList<>(); - dep4.add("1"); - node4.setPreTasks(JSONUtils.toJsonString(dep4)); - taskNodeList.add(node4); - - TaskNode node5 = new TaskNode(); - node5.setId("5"); - node5.setName("5"); - node5.setCode(5); - node5.setType("SHELL"); - List dep5 = new ArrayList<>(); - dep5.add(1L); - node5.setPreTasks(JSONUtils.toJsonString(dep5)); - taskNodeList.add(node5); - - TaskNode node6 = new TaskNode(); - node5.setId("6"); - node5.setName("6"); - node5.setCode(6); - node5.setType("SHELL"); - List dep6 = new ArrayList<>(); - dep5.add(2L); - dep5.add(4L); - node5.setPreTasks(JSONUtils.toJsonString(dep6)); - taskNodeList.add(node6); - - List startNodes = new ArrayList<>(); - List recoveryNodes = new ArrayList<>(); - List destTaskNodeList = DagHelper.generateFlowNodeListByStartNode(taskNodeList, - startNodes, recoveryNodes, TaskDependType.TASK_POST); - List taskNodeRelations = DagHelper.generateRelationListByFlowNodes(destTaskNodeList); - ProcessDag processDag = new ProcessDag(); - processDag.setEdges(taskNodeRelations); - processDag.setNodes(destTaskNodeList); - return DagHelper.buildDagGraph(processDag); - } - - private SwitchParameters getSwitchNode() { - SwitchParameters conditionsParameters = new SwitchParameters(); - SwitchResultVo switchResultVo1 = new SwitchResultVo(); - switchResultVo1.setCondition(" 2 == 1"); - switchResultVo1.setNextNode(2L); - SwitchResultVo switchResultVo2 = new SwitchResultVo(); - switchResultVo2.setCondition(" 2 == 2"); - switchResultVo2.setNextNode(4L); - List list = new ArrayList<>(); - list.add(switchResultVo1); - list.add(switchResultVo2); - conditionsParameters.setDependTaskList(list); - conditionsParameters.setNextNode(5L); - conditionsParameters.setRelation("AND"); - conditionsParameters.setResultConditionLocation(1); - // in: AND(AND(1 is SUCCESS)) - return conditionsParameters; - } - @Test public void testBuildDagGraph() { String shellJson = diff --git a/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/enums/ResourceType.java b/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/enums/ResourceType.java index 16c49a1ecc..2dd7ca0671 100644 --- a/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/enums/ResourceType.java +++ b/dolphinscheduler-spi/src/main/java/org/apache/dolphinscheduler/spi/enums/ResourceType.java @@ -47,4 +47,13 @@ public enum ResourceType { public String getDescp() { return descp; } + + public static ResourceType getResourceType(int code) { + for (ResourceType resourceType : ResourceType.values()) { + if (resourceType.getCode() == code) { + return resourceType; + } + } + return null; + } } diff --git a/dolphinscheduler-standalone-server/src/main/resources/application.yaml b/dolphinscheduler-standalone-server/src/main/resources/application.yaml index 1c6da324ee..906fc42085 100644 --- a/dolphinscheduler-standalone-server/src/main/resources/application.yaml +++ b/dolphinscheduler-standalone-server/src/main/resources/application.yaml @@ -64,6 +64,7 @@ spring: mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER + cloud.discovery.client.composite-indicator.enabled: false mybatis-plus: mapper-locations: classpath:org/apache/dolphinscheduler/dao/mapper/*Mapper.xml @@ -84,12 +85,12 @@ registry: namespace: dolphinscheduler connect-string: localhost:2181 retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms + base-sleep-time: 1s + max-sleep: 3s max-retries: 5 - session-timeout: 30s - connection-timeout: 9s - block-until-connected: 600ms + session-timeout: 60s + connection-timeout: 15s + block-until-connected: 15s digest: ~ security: diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperator.java index d470545bab..81b93ea207 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperator.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperator.java @@ -19,17 +19,12 @@ package org.apache.dolphinscheduler.plugin.storage.abs; import static org.apache.dolphinscheduler.common.constants.Constants.EMPTY_STRING; import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_FILE; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_UDF; import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; import org.apache.dolphinscheduler.common.utils.FileUtils; -import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.AbstractStorageOperator; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.spi.enums.ResourceType; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.lang3.StringUtils; @@ -37,136 +32,69 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.sql.Date; -import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.Data; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import com.azure.core.http.rest.PagedIterable; import com.azure.storage.blob.BlobClient; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; import com.azure.storage.blob.BlobServiceClientBuilder; import com.azure.storage.blob.models.BlobContainerItem; -import com.azure.storage.blob.models.BlobItem; import com.azure.storage.blob.specialized.BlockBlobClient; -@Data @Slf4j -public class AbsStorageOperator implements Closeable, StorageOperate { +public class AbsStorageOperator extends AbstractStorageOperator implements Closeable, StorageOperator { - private BlobContainerClient blobContainerClient; + private final BlobContainerClient blobContainerClient; - private BlobServiceClient blobServiceClient; + private final BlobServiceClient blobServiceClient; - private String connectionString; - - private String storageAccountName; - - private String containerName; - - public AbsStorageOperator() { - - } - - public void init() { - containerName = readContainerName(); - connectionString = readConnectionString(); - storageAccountName = readAccountName(); - blobServiceClient = buildBlobServiceClient(); - blobContainerClient = buildBlobContainerClient(); - checkContainerNameExists(); - } - - protected BlobServiceClient buildBlobServiceClient() { - return new BlobServiceClientBuilder() - .endpoint("https://" + storageAccountName + ".blob.core.windows.net/") - .connectionString(connectionString) + public AbsStorageOperator(AbsStorageProperties absStorageProperties) { + super(absStorageProperties.getResourceUploadPath()); + blobServiceClient = new BlobServiceClientBuilder() + .endpoint("https://" + absStorageProperties.getStorageAccountName() + ".blob.core.windows.net/") + .connectionString(absStorageProperties.getConnectionString()) .buildClient(); - } - - protected BlobContainerClient buildBlobContainerClient() { - return blobServiceClient.getBlobContainerClient(containerName); - } - - protected String readConnectionString() { - return PropertyUtils.getString(Constants.AZURE_BLOB_STORAGE_CONNECTION_STRING); - } - - protected String readContainerName() { - return PropertyUtils.getString(Constants.AZURE_BLOB_STORAGE_CONTAINER_NAME); - } - - protected String readAccountName() { - return PropertyUtils.getString(Constants.AZURE_BLOB_STORAGE_ACCOUNT_NAME); + blobContainerClient = blobServiceClient.getBlobContainerClient(absStorageProperties.getContainerName()); + checkContainerNameExists(absStorageProperties.getContainerName()); } @Override - public void createTenantDirIfNotExists(String tenantCode) throws Exception { - mkdir(tenantCode, getAbsResDir(tenantCode)); - mkdir(tenantCode, getAbsUdfDir(tenantCode)); - } - - public String getAbsResDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_FILE, getAbsTenantDir(tenantCode)); - } - - public String getAbsUdfDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_UDF, getAbsTenantDir(tenantCode)); - } - - public String getAbsTenantDir(String tenantCode) { - return String.format(FORMAT_S_S, getGcsDataBasePath(), tenantCode); - } - - public String getGcsDataBasePath() { - if (FOLDER_SEPARATOR.equals(RESOURCE_UPLOAD_PATH)) { - return EMPTY_STRING; - } else { - return RESOURCE_UPLOAD_PATH.replaceFirst(FOLDER_SEPARATOR, EMPTY_STRING); + public String getStorageBaseDirectory() { + // All directory should end with File.separator + if (getStorageBaseDirectory().startsWith("/")) { + log.warn("{} -> {} should not start with / in abs", Constants.RESOURCE_UPLOAD_PATH, + getStorageBaseDirectory()); + return getStorageBaseDirectory().substring(1); } + return getStorageBaseDirectory(); } + @SneakyThrows @Override - public String getResDir(String tenantCode) { - return getAbsResDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public String getUdfDir(String tenantCode) { - return getAbsUdfDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public String getResourceFullName(String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName.replaceFirst(FOLDER_SEPARATOR, EMPTY_STRING); + public void createStorageDir(String directory) { + String objectName = directory + FOLDER_SEPARATOR; + if (isObjectExists(objectName)) { + throw new FileAlreadyExistsException("directory: " + objectName + " already exists"); } - return String.format(FORMAT_S_S, getAbsResDir(tenantCode), fileName); + BlobClient blobClient = blobContainerClient.getBlobClient(objectName); + blobClient.upload(new ByteArrayInputStream(EMPTY_STRING.getBytes()), 0); } + @SneakyThrows @Override - public String getFileName(ResourceType resourceType, String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, EMPTY_STRING); - } - return getDir(resourceType, tenantCode) + fileName; - } - - @Override - public void download(String srcFilePath, String dstFilePath, boolean overwrite) throws IOException { + public void download(String srcFilePath, String dstFilePath, boolean overwrite) { File dstFile = new File(dstFilePath); if (dstFile.isDirectory()) { Files.delete(dstFile.toPath()); @@ -179,7 +107,7 @@ public class AbsStorageOperator implements Closeable, StorageOperate { } @Override - public boolean exists(String fullName) throws IOException { + public boolean exists(String fullName) { return isObjectExists(fullName); } @@ -187,36 +115,14 @@ public class AbsStorageOperator implements Closeable, StorageOperate { return blobContainerClient.getBlobClient(objectName).exists(); } + @SneakyThrows @Override - public boolean delete(String filePath, boolean recursive) throws IOException { - try { - if (isObjectExists(filePath)) { - blobContainerClient.getBlobClient(filePath).delete(); - } - return true; - } catch (Exception e) { - log.error("delete the object error,the resource path is {}", filePath); - return false; - } + public void delete(String filePath, boolean recursive) { + blobContainerClient.getBlobClient(filePath).deleteIfExists(); } @Override - public boolean delete(String fullName, List childrenPathList, boolean recursive) throws IOException { - // append the resource fullName to the list for deletion. - childrenPathList.add(fullName); - - boolean result = true; - for (String filePath : childrenPathList) { - if (!delete(filePath, recursive)) { - result = false; - } - } - - return result; - } - - @Override - public boolean copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) throws IOException { + public void copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) { BlobClient srcBlobClient = blobContainerClient.getBlobClient(srcPath); BlockBlobClient dstBlobClient = blobContainerClient.getBlobClient(dstPath).getBlockBlobClient(); @@ -225,29 +131,23 @@ public class AbsStorageOperator implements Closeable, StorageOperate { if (deleteSource) { srcBlobClient.delete(); } - return true; } + @SneakyThrows @Override - public boolean upload(String tenantCode, String srcFile, String dstPath, boolean deleteSource, - boolean overwrite) throws IOException { - try { - BlobClient blobClient = blobContainerClient.getBlobClient(dstPath); - blobClient.uploadFromFile(srcFile, overwrite); + public void upload(String srcFile, String dstPath, boolean deleteSource, boolean overwrite) { + BlobClient blobClient = blobContainerClient.getBlobClient(dstPath); + blobClient.uploadFromFile(srcFile, overwrite); - Path srcPath = Paths.get(srcFile); - if (deleteSource) { - Files.delete(srcPath); - } - return true; - } catch (Exception e) { - log.error("upload failed,the container is {},the filePath is {}", containerName, dstPath); - return false; + Path srcPath = Paths.get(srcFile); + if (deleteSource) { + Files.delete(srcPath); } } + @SneakyThrows @Override - public List vimFile(String tenantCode, String filePath, int skipLineNums, int limit) throws IOException { + public List fetchFileContent(String filePath, int skipLineNums, int limit) { if (StringUtils.isBlank(filePath)) { log.error("file path:{} is blank", filePath); return Collections.emptyList(); @@ -263,199 +163,27 @@ public class AbsStorageOperator implements Closeable, StorageOperate { } } - @Override - public void deleteTenant(String tenantCode) throws Exception { - deleteTenantCode(tenantCode); - } - - protected void deleteTenantCode(String tenantCode) { - deleteDirectory(getResDir(tenantCode)); - deleteDirectory(getUdfDir(tenantCode)); - } - - @Override - public String getDir(ResourceType resourceType, String tenantCode) { - switch (resourceType) { - case UDF: - return getUdfDir(tenantCode); - case FILE: - return getResDir(tenantCode); - case ALL: - return getGcsDataBasePath(); - default: - return EMPTY_STRING; - } - - } - - protected void deleteDirectory(String directoryName) { - if (isObjectExists(directoryName)) { - blobContainerClient.getBlobClient(directoryName).delete(); - } - } - - @Override - public boolean mkdir(String tenantCode, String path) throws IOException { - String objectName = path + FOLDER_SEPARATOR; - if (!isObjectExists(objectName)) { - BlobClient blobClient = blobContainerClient.getBlobClient(objectName); - blobClient.upload(new ByteArrayInputStream(EMPTY_STRING.getBytes()), 0); - } - return true; - } - @Override public void close() throws IOException { } @Override - public ResUploadType returnStorageType() { - return ResUploadType.ABS; + public List listStorageEntity(String resourceAbsolutePath) { + return null; } @Override - public List listFilesStatusRecursively(String path, String defaultPath, String tenantCode, - ResourceType type) { - List storageEntityList = new ArrayList<>(); - LinkedList foldersToFetch = new LinkedList<>(); - - StorageEntity initialEntity = null; - try { - initialEntity = getFileStatus(path, defaultPath, tenantCode, type); - } catch (Exception e) { - log.error("error while listing files status recursively, path: {}", path, e); - return storageEntityList; - } - foldersToFetch.add(initialEntity); - - while (!foldersToFetch.isEmpty()) { - String pathToExplore = foldersToFetch.pop().getFullName(); - try { - List tempList = listFilesStatus(pathToExplore, defaultPath, tenantCode, type); - for (StorageEntity temp : tempList) { - if (temp.isDirectory()) { - foldersToFetch.add(temp); - } - } - storageEntityList.addAll(tempList); - } catch (Exception e) { - log.error("error while listing files stat:wus recursively, path: {}", pathToExplore, e); - } - } - - return storageEntityList; + public List listFileStorageEntityRecursively(String resourceAbsolutePath) { + return null; } @Override - public List listFilesStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception { - List storageEntityList = new ArrayList<>(); - - PagedIterable blobItems; - blobItems = blobContainerClient.listBlobsByHierarchy(path); - if (blobItems == null) { - return storageEntityList; - } - - for (BlobItem blobItem : blobItems) { - if (path.equals(blobItem.getName())) { - continue; - } - if (blobItem.isPrefix()) { - String suffix = StringUtils.difference(path, blobItem.getName()); - String fileName = StringUtils.difference(defaultPath, blobItem.getName()); - StorageEntity entity = new StorageEntity(); - entity.setAlias(suffix); - entity.setFileName(fileName); - entity.setFullName(blobItem.getName()); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - entity.setCreateTime(null); - entity.setUpdateTime(null); - entity.setPfullName(path); - - storageEntityList.add(entity); - } else { - String[] aliasArr = blobItem.getName().split("/"); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, blobItem.getName()); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(blobItem.getName()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(blobItem.getProperties().getContentLength()); - entity.setCreateTime(Date.from(blobItem.getProperties().getCreationTime().toInstant())); - entity.setUpdateTime(Date.from(blobItem.getProperties().getLastModified().toInstant())); - entity.setPfullName(path); - - storageEntityList.add(entity); - } - } - - return storageEntityList; + public StorageEntity getStorageEntity(String resourceAbsolutePath) { + return null; } - @Override - public StorageEntity getFileStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception { - if (path.endsWith(FOLDER_SEPARATOR)) { - // the path is a directory that may or may not exist - String alias = findDirAlias(path); - String fileName = StringUtils.difference(defaultPath, path); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(path); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - - return entity; - } else { - if (isObjectExists(path)) { - BlobClient blobClient = blobContainerClient.getBlobClient(path); - - String[] aliasArr = blobClient.getBlobName().split(FOLDER_SEPARATOR); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, blobClient.getBlobName()); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(blobClient.getBlobName()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(blobClient.getProperties().getBlobSize()); - entity.setCreateTime(Date.from(blobClient.getProperties().getCreationTime().toInstant())); - entity.setUpdateTime(Date.from(blobClient.getProperties().getLastModified().toInstant())); - - return entity; - } else { - throw new FileNotFoundException("Object is not found in ABS container: " + containerName); - } - } - } - - private String findDirAlias(String dirPath) { - if (!dirPath.endsWith(FOLDER_SEPARATOR)) { - return dirPath; - } - - Path path = Paths.get(dirPath); - return path.getName(path.getNameCount() - 1) + FOLDER_SEPARATOR; - } - - public void checkContainerNameExists() { + public void checkContainerNameExists(String containerName) { if (StringUtils.isBlank(containerName)) { throw new IllegalArgumentException(containerName + " is blank"); } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperatorFactory.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperatorFactory.java index 1909ece6f1..6428ec0980 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperatorFactory.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperatorFactory.java @@ -17,20 +17,30 @@ package org.apache.dolphinscheduler.plugin.storage.abs; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperateFactory; +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperatorFactory; import org.apache.dolphinscheduler.plugin.storage.api.StorageType; import com.google.auto.service.AutoService; -@AutoService(StorageOperateFactory.class) -public class AbsStorageOperatorFactory implements StorageOperateFactory { +@AutoService(StorageOperatorFactory.class) +public class AbsStorageOperatorFactory implements StorageOperatorFactory { @Override - public StorageOperate createStorageOperate() { - AbsStorageOperator absStorageOperator = new AbsStorageOperator(); - absStorageOperator.init(); - return absStorageOperator; + public StorageOperator createStorageOperate() { + final AbsStorageProperties absStorageProperties = getAbsStorageProperties(); + return new AbsStorageOperator(absStorageProperties); + } + + private AbsStorageProperties getAbsStorageProperties() { + return AbsStorageProperties.builder() + .containerName(PropertyUtils.getString(Constants.AZURE_BLOB_STORAGE_CONTAINER_NAME)) + .connectionString(PropertyUtils.getString(Constants.AZURE_BLOB_STORAGE_CONNECTION_STRING)) + .storageAccountName(PropertyUtils.getString(Constants.AZURE_BLOB_STORAGE_ACCOUNT_NAME)) + .resourceUploadPath(PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler")) + .build(); } @Override diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageProperties.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageProperties.java new file mode 100644 index 0000000000..e33e165a9b --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/main/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageProperties.java @@ -0,0 +1,36 @@ +/* + * 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.dolphinscheduler.plugin.storage.abs; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AbsStorageProperties { + + private String containerName; + private String connectionString; + private String storageAccountName; + private String resourceUploadPath; + +} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/test/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperatorTest.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/test/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperatorTest.java deleted file mode 100644 index daec0c36b2..0000000000 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-abs/src/test/java/org/apache/dolphinscheduler/plugin/storage/abs/AbsStorageOperatorTest.java +++ /dev/null @@ -1,274 +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.dolphinscheduler.plugin.storage.abs; - -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.spi.enums.ResourceType; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.azure.storage.blob.BlobClient; -import com.azure.storage.blob.BlobContainerClient; -import com.azure.storage.blob.BlobServiceClient; -import com.azure.storage.blob.specialized.BlockBlobClient; - -@ExtendWith(MockitoExtension.class) -public class AbsStorageOperatorTest { - - private static final String CONNECTION_STRING_MOCK = "CONNECTION_STRING_MOCK"; - - private static final String ACCOUNT_NAME_MOCK = "ACCOUNT_NAME_MOCK"; - - private static final String CONTAINER_NAME_MOCK = "CONTAINER_NAME_MOCK"; - - private static final String TENANT_CODE_MOCK = "TENANT_CODE_MOCK"; - - private static final String DIR_MOCK = "DIR_MOCK"; - - private static final String FILE_NAME_MOCK = "FILE_NAME_MOCK"; - - private static final String FILE_PATH_MOCK = "FILE_PATH_MOCK"; - - private static final String FULL_NAME = "/tmp/dir1/"; - - private static final String DEFAULT_PATH = "/tmp/"; - - @Mock - private BlobContainerClient blobContainerClient; - - @Mock - private BlobServiceClient blobServiceClient; - - @Mock - private BlockBlobClient blockBlobClient; - - @Mock - private BlobClient blobClient; - - private AbsStorageOperator absStorageOperator; - - @BeforeEach - public void setUp() throws Exception { - absStorageOperator = Mockito.spy(AbsStorageOperator.class); - Mockito.doReturn(CONNECTION_STRING_MOCK).when(absStorageOperator).readConnectionString(); - Mockito.doReturn(CONTAINER_NAME_MOCK).when(absStorageOperator).readContainerName(); - Mockito.doReturn(ACCOUNT_NAME_MOCK).when(absStorageOperator).readAccountName(); - Mockito.doReturn(blobContainerClient).when(absStorageOperator).buildBlobContainerClient(); - Mockito.doReturn(blobServiceClient).when(absStorageOperator).buildBlobServiceClient(); - Mockito.doNothing().when(absStorageOperator).checkContainerNameExists(); - - absStorageOperator.init(); - } - - @Test - public void testInit() throws Exception { - verify(absStorageOperator, times(1)).buildBlobServiceClient(); - verify(absStorageOperator, times(1)).buildBlobContainerClient(); - Assertions.assertEquals(CONNECTION_STRING_MOCK, absStorageOperator.getConnectionString()); - Assertions.assertEquals(CONTAINER_NAME_MOCK, absStorageOperator.getContainerName()); - Assertions.assertEquals(ACCOUNT_NAME_MOCK, absStorageOperator.getStorageAccountName()); - } - - @Test - public void createTenantResAndUdfDir() throws Exception { - doReturn(DIR_MOCK).when(absStorageOperator).getAbsResDir(TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(absStorageOperator).getAbsUdfDir(TENANT_CODE_MOCK); - doReturn(true).when(absStorageOperator).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - absStorageOperator.createTenantDirIfNotExists(TENANT_CODE_MOCK); - verify(absStorageOperator, times(2)).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - } - - @Test - public void getResDir() { - final String expectedResourceDir = String.format("dolphinscheduler/%s/resources/", TENANT_CODE_MOCK); - final String dir = absStorageOperator.getResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedResourceDir, dir); - } - - @Test - public void getUdfDir() { - final String expectedUdfDir = String.format("dolphinscheduler/%s/udfs/", TENANT_CODE_MOCK); - final String dir = absStorageOperator.getUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedUdfDir, dir); - } - - @Test - public void mkdirWhenDirExists() { - boolean isSuccess = false; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - Mockito.doReturn(true).when(absStorageOperator).isObjectExists(key); - isSuccess = absStorageOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); - } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void getResourceFullName() { - final String expectedResourceFileName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFileName = absStorageOperator.getResourceFullName(TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedResourceFileName, resourceFileName); - } - - @Test - public void getFileName() { - final String expectedFileName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String fileName = absStorageOperator.getFileName(ResourceType.FILE, TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedFileName, fileName); - } - - @Test - public void exists() { - boolean doesExist = false; - doReturn(true).when(absStorageOperator).isObjectExists(FILE_NAME_MOCK); - try { - doesExist = absStorageOperator.exists(FILE_NAME_MOCK); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(doesExist); - } - - @Test - public void delete() { - boolean isDeleted = false; - doReturn(true).when(absStorageOperator).isObjectExists(FILE_NAME_MOCK); - Mockito.doReturn(blobClient).when(blobContainerClient).getBlobClient(Mockito.anyString()); - try { - isDeleted = absStorageOperator.delete(FILE_NAME_MOCK, true); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isDeleted); - verify(blobClient, times(1)).delete(); - } - - @Test - public void copy() { - boolean isSuccess = false; - Mockito.doReturn(blobClient).when(blobContainerClient).getBlobClient(Mockito.anyString()); - Mockito.doReturn(blockBlobClient).when(blobClient).getBlockBlobClient(); - try { - isSuccess = absStorageOperator.copy(FILE_PATH_MOCK, FILE_PATH_MOCK, false, false); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void deleteTenant() { - doNothing().when(absStorageOperator).deleteTenantCode(anyString()); - try { - absStorageOperator.deleteTenant(TENANT_CODE_MOCK); - } catch (Exception e) { - Assertions.fail("unexpected exception caught in unit test"); - } - - verify(absStorageOperator, times(1)).deleteTenantCode(anyString()); - } - - @Test - public void getGcsResDir() { - final String expectedGcsResDir = String.format("dolphinscheduler/%s/resources", TENANT_CODE_MOCK); - final String gcsResDir = absStorageOperator.getAbsResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedGcsResDir, gcsResDir); - } - - @Test - public void getGcsUdfDir() { - final String expectedGcsUdfDir = String.format("dolphinscheduler/%s/udfs", TENANT_CODE_MOCK); - final String gcsUdfDir = absStorageOperator.getAbsUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedGcsUdfDir, gcsUdfDir); - } - - @Test - public void getGcsTenantDir() { - final String expectedGcsTenantDir = String.format(FORMAT_S_S, DIR_MOCK, TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(absStorageOperator).getGcsDataBasePath(); - final String gcsTenantDir = absStorageOperator.getAbsTenantDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedGcsTenantDir, gcsTenantDir); - } - - @Test - public void deleteDir() { - Mockito.doReturn(blobClient).when(blobContainerClient).getBlobClient(Mockito.anyString()); - doReturn(true).when(absStorageOperator).isObjectExists(Mockito.any()); - absStorageOperator.deleteDirectory(DIR_MOCK); - verify(blobClient, times(1)).delete(); - } - - @Test - public void testGetFileStatus() throws Exception { - StorageEntity entity = - absStorageOperator.getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(FULL_NAME, entity.getFullName()); - Assertions.assertEquals("dir1/", entity.getFileName()); - } - - @Test - public void testListFilesStatus() throws Exception { - Mockito.doReturn(null).when(blobContainerClient).listBlobsByHierarchy(Mockito.any()); - List result = - absStorageOperator.listFilesStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - verify(blobContainerClient, times(1)).listBlobsByHierarchy(Mockito.any()); - } - - @Test - public void testListFilesStatusRecursively() throws Exception { - StorageEntity entity = new StorageEntity(); - entity.setFullName(FULL_NAME); - - doReturn(entity).when(absStorageOperator).getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, - ResourceType.FILE); - doReturn(Collections.EMPTY_LIST).when(absStorageOperator).listFilesStatus(anyString(), anyString(), anyString(), - Mockito.any(ResourceType.class)); - - List result = - absStorageOperator.listFilesStatusRecursively(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, - ResourceType.FILE); - Assertions.assertEquals(0, result.size()); - } -} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/AbstractStorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/AbstractStorageOperator.java new file mode 100644 index 0000000000..1ad94e47d9 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/AbstractStorageOperator.java @@ -0,0 +1,110 @@ +/* + * 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.dolphinscheduler.plugin.storage.api; + +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.FileUtils; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import org.apache.commons.lang3.StringUtils; + +import java.io.File; + +import com.google.common.base.Preconditions; +import com.google.common.io.Files; + +public abstract class AbstractStorageOperator implements StorageOperator { + + protected final String resourceBaseAbsolutePath; + + public AbstractStorageOperator(String resourceBaseAbsolutePath) { + Preconditions.checkNotNull(resourceBaseAbsolutePath, "Resource upload path should not be null"); + this.resourceBaseAbsolutePath = resourceBaseAbsolutePath; + } + + @Override + public ResourceMetadata getResourceMetaData(String resourceAbsolutePath) { + String storageBaseDirectory = getStorageBaseDirectory(); + String resourceSegment = StringUtils.substringAfter(resourceAbsolutePath, storageBaseDirectory); + String[] segments = StringUtils.split(resourceSegment, File.separator, 3); + if (segments.length == 0) { + throw new IllegalArgumentException("Invalid resource path: " + resourceAbsolutePath); + } + return ResourceMetadata.builder() + .resourceAbsolutePath(resourceAbsolutePath) + .resourceBaseDirectory(storageBaseDirectory) + .isDirectory(Files.getFileExtension(resourceAbsolutePath).isEmpty()) + .tenant(segments[0]) + .resourceType(segments[1].equals(FILE_FOLDER_NAME) ? ResourceType.FILE : ResourceType.UDF) + .resourceRelativePath(segments.length == 2 ? "/" : segments[2]) + .resourceParentAbsolutePath(StringUtils.substringBeforeLast(resourceAbsolutePath, File.separator)) + .build(); + } + + @Override + public String getStorageBaseDirectory() { + // All directory should end with File.separator + return PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler"); + } + + @Override + public String getStorageBaseDirectory(String tenantCode) { + if (StringUtils.isEmpty(tenantCode)) { + throw new IllegalArgumentException("Tenant code should not be empty"); + } + // All directory should end with File.separator + return FileUtils.concatFilePath(getStorageBaseDirectory(), tenantCode); + } + + @Override + public String getStorageBaseDirectory(String tenantCode, ResourceType resourceType) { + String tenantBaseDirectory = getStorageBaseDirectory(tenantCode); + if (resourceType == null) { + throw new IllegalArgumentException("Resource type should not be null"); + } + String resourceBaseDirectory; + switch (resourceType) { + case FILE: + resourceBaseDirectory = FileUtils.concatFilePath(tenantBaseDirectory, FILE_FOLDER_NAME); + break; + case UDF: + resourceBaseDirectory = FileUtils.concatFilePath(tenantBaseDirectory, UDF_FOLDER_NAME); + break; + case ALL: + resourceBaseDirectory = tenantBaseDirectory; + break; + default: + throw new IllegalArgumentException("Resource type: " + resourceType + " not supported"); + } + // All directory should end with File.separator + return resourceBaseDirectory; + } + + @Override + public String getStorageFileAbsolutePath(String tenantCode, String fileName) { + return FileUtils.concatFilePath(getStorageBaseDirectory(tenantCode, ResourceType.FILE), fileName); + } + + protected void exceptionIfPathEmpty(String resourceAbsolutePath) { + if (StringUtils.isEmpty(resourceAbsolutePath)) { + throw new IllegalArgumentException("Resource path should not be empty"); + } + } + +} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/ResourceMetadata.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/ResourceMetadata.java new file mode 100644 index 0000000000..6a2ee3dc63 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/ResourceMetadata.java @@ -0,0 +1,42 @@ +/* + * 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.dolphinscheduler.plugin.storage.api; + +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ResourceMetadata { + + private String resourceAbsolutePath; + + private String resourceBaseDirectory; + private String tenant; + private ResourceType resourceType; + private String resourceRelativePath; + private String resourceParentAbsolutePath; + private boolean isDirectory; + +} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageConfiguration.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageConfiguration.java index d34e6d01de..9b30ddb450 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageConfiguration.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageConfiguration.java @@ -32,13 +32,13 @@ import org.springframework.context.annotation.Configuration; public class StorageConfiguration { @Bean - public StorageOperate storageOperate() { + public StorageOperator storageOperate() { Optional storageTypeOptional = StorageType.getStorageType(PropertyUtils.getUpperCaseString(RESOURCE_STORAGE_TYPE)); - Optional storageOperate = storageTypeOptional.map(storageType -> { - ServiceLoader storageOperateFactories = - ServiceLoader.load(StorageOperateFactory.class); - for (StorageOperateFactory storageOperateFactory : storageOperateFactories) { + Optional storageOperate = storageTypeOptional.map(storageType -> { + ServiceLoader storageOperateFactories = + ServiceLoader.load(StorageOperatorFactory.class); + for (StorageOperatorFactory storageOperateFactory : storageOperateFactories) { if (storageOperateFactory.getStorageOperate() == storageType) { return storageOperateFactory.createStorageOperate(); } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageEntity.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageEntity.java index e3639b8afe..cae9d862a0 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageEntity.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageEntity.java @@ -24,7 +24,10 @@ import org.apache.dolphinscheduler.spi.enums.ResourceType; import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; // StorageEneity is an entity representing a resource in the third-part storage service. // It is only stored in t_ds_relation_resources_task if the resource is used by a task. @@ -32,32 +35,16 @@ import lombok.Data; // in table t_ds_relation_resources_task. @Data +@NoArgsConstructor +@AllArgsConstructor +@Builder public class StorageEntity { - /** - * exist only if it is stored in t_ds_relation_resources_task. - * - */ - private int id; - /** - * fullname is in a format of basepath + tenantCode + res/udf + filename - */ private String fullName; - /** - * filename is in a format of possible parent folders + alias - */ + private String fileName; - /** - * the name of the file - */ - private String alias; - /** - * parent folder time - */ private String pfullName; private boolean isDirectory; - private int userId; - private String userName; private ResourceType type; private long size; private Date createTime; diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperate.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperate.java deleted file mode 100644 index 945e361a09..0000000000 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperate.java +++ /dev/null @@ -1,203 +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.dolphinscheduler.plugin.storage.api; - -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_FILE; - -import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; -import org.apache.dolphinscheduler.common.utils.PropertyUtils; -import org.apache.dolphinscheduler.spi.enums.ResourceType; - -import java.io.IOException; -import java.util.List; - -public interface StorageOperate { - - String RESOURCE_UPLOAD_PATH = PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler"); - - /** - * if the resource of tenant 's exist, the resource of folder will be created - * @param tenantCode - * @throws Exception - */ - void createTenantDirIfNotExists(String tenantCode) throws Exception; - - /** - * get the resource directory of tenant - * @param tenantCode - * @return - */ - String getResDir(String tenantCode); - - /** - * return the udf directory of tenant - * @param tenantCode - * @return - */ - String getUdfDir(String tenantCode); - - /** - * create the directory that the path of tenant wanted to create - * @param tenantCode - * @param path - * @return - * @throws IOException - */ - boolean mkdir(String tenantCode, String path) throws IOException; - - /** - * get the path of the resource file (fullName) - * @param tenantCode - * @param fileName - * @return - */ - String getResourceFullName(String tenantCode, String fileName); - - /** - * get the path of the resource file excluding the base path (fileName) - */ - default String getResourceFileName(String tenantCode, String fullName) { - String resDir = getResDir(tenantCode); - String filenameReplaceResDir = fullName.replaceFirst(resDir, ""); - if (!filenameReplaceResDir.equals(fullName)) { - return filenameReplaceResDir; - } - - // Replace resource dir not effective in case of run workflow with different tenant from resource file's. - // this is backup solution to get related path, by split with RESOURCE_TYPE_FILE - return filenameReplaceResDir.contains(RESOURCE_TYPE_FILE) - ? filenameReplaceResDir.split(String.format("%s/", RESOURCE_TYPE_FILE))[1] - : filenameReplaceResDir; - } - - /** - * get the path of the file - * @param resourceType - * @param tenantCode - * @param fileName - * @return - */ - String getFileName(ResourceType resourceType, String tenantCode, String fileName); - - /** - * predicate if the resource of tenant exists - * @param fullName - * @return - * @throws IOException - */ - boolean exists(String fullName) throws IOException; - - /** - * delete the resource of filePath - * todo if the filePath is the type of directory ,the files in the filePath need to be deleted at all - * @param filePath - * @param recursive - * @return - * @throws IOException - */ - boolean delete(String filePath, boolean recursive) throws IOException; - - boolean delete(String filePath, List childrenPathArray, boolean recursive) throws IOException; - - /** - * copy the file from srcPath to dstPath - * @param srcPath - * @param dstPath - * @param deleteSource if need to delete the file of srcPath - * @param overwrite - * @return - * @throws IOException - */ - boolean copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) throws IOException; - - /** - * get the root path of the tenant with resourceType - * @param resourceType - * @param tenantCode - * @return - */ - String getDir(ResourceType resourceType, String tenantCode); - - /** - * upload the local srcFile to dstPath - * @param tenantCode - * @param srcFile - * @param dstPath - * @param deleteSource - * @param overwrite - * @return - * @throws IOException - */ - boolean upload(String tenantCode, String srcFile, String dstPath, boolean deleteSource, - boolean overwrite) throws IOException; - - /** - * download the srcPath to local - * - * @param srcFilePath the full path of the srcPath - * @param dstFile - * @param overwrite - * @throws IOException - */ - void download(String srcFilePath, String dstFile, boolean overwrite) throws IOException; - - /** - * vim the context of filePath - * @param tenantCode - * @param filePath - * @param skipLineNums - * @param limit - * @return - * @throws IOException - */ - List vimFile(String tenantCode, String filePath, int skipLineNums, int limit) throws IOException; - - /** - * delete the files and directory of the tenant - * - * @param tenantCode - * @throws Exception - */ - void deleteTenant(String tenantCode) throws Exception; - - /** - * return the storageType - * - * @return - */ - ResUploadType returnStorageType(); - - /** - * return files and folders in the current directory and subdirectories - * */ - List listFilesStatusRecursively(String path, String defaultPath, String tenantCode, - ResourceType type); - - /** - * return files and folders in the current directory - * */ - List listFilesStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception; - - /** - * return a file status - * */ - StorageEntity getFileStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception; -} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperator.java new file mode 100644 index 0000000000..fb27bba217 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperator.java @@ -0,0 +1,157 @@ +/* + * 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.dolphinscheduler.plugin.storage.api; + +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import java.nio.file.FileAlreadyExistsException; +import java.util.List; + +public interface StorageOperator { + + String FILE_FOLDER_NAME = "resources"; + String UDF_FOLDER_NAME = "udfs"; + + ResourceMetadata getResourceMetaData(String resourceAbsolutePath); + + /** + * Get the absolute path of base directory. + * + * @return the base directory. e.g. file:///tmp/dolphinscheduler/, /tmp/dolphinscheduler/ + */ + String getStorageBaseDirectory(); + + /** + * Get the absolute path of directory which will be used by the given tenant. the tenant directory is under the base directory. + * + * @param tenantCode the tenant code, cannot be empty + * @return the tenant directory. e.g. file:///tmp/dolphinscheduler/default/ + */ + String getStorageBaseDirectory(String tenantCode); + + /** + * Get the absolute path of directory which will be used by the given tenant and resource type. the resource directory is under the tenant directory. + *

If the resource type is FILE, will be 'file:///tmp/dolphinscheduler/default/resources/'. + *

If the resource type is UDF, will be 'is file:///tmp/dolphinscheduler/default/udfs/'. + *

If the resource type is ALL, will be 'is file:///tmp/dolphinscheduler/default/'. + * + * @param tenantCode the tenant code, cannot be empty + * @param resourceType the resource type, cannot be null + * @return the resource directory. e.g. file:///tmp/dolphinscheduler/default/resources/ + */ + String getStorageBaseDirectory(String tenantCode, ResourceType resourceType); + + /** + * Get the absolute path of the file in the storage. the file will under the file resource directory. + * + * @param tenantCode the tenant code, cannot be empty + * @param fileName the file name, cannot be empty + * @return the file absolute path. e.g. file:///tmp/dolphinscheduler/default/resources/test.sh + */ + String getStorageFileAbsolutePath(String tenantCode, String fileName); + + /** + * Create a directory if the directory is already exists will throw exception(Dependent on the storage implementation). + *

If the directory is not exists, will create the directory. + *

If the parent directory is not exists, will create the parent directory. + *

If the directory is already exists, will throw {@link FileAlreadyExistsException}. + * + * @param directoryAbsolutePath the directory absolute path + */ + void createStorageDir(String directoryAbsolutePath); + + /** + * Check if the resource exists. + * + * @param resourceAbsolutePath the resource absolute path + * @return true if the resource exists, otherwise false + */ + boolean exists(String resourceAbsolutePath); + + /** + * Delete the resource, if the resourceAbsolutePath is not exists, will do nothing. + * + * @param resourceAbsolutePath the resource absolute path + * @param recursive whether to delete all the sub file/directory under the given resource + */ + void delete(String resourceAbsolutePath, boolean recursive); + + /** + * Copy the resource from the source path to the destination path. + * + * @param srcAbsolutePath the source path + * @param dstAbsolutePath the destination path + * @param deleteSource whether to delete the source path after copying + * @param overwrite whether to overwrite the destination path if it exists + */ + void copy(String srcAbsolutePath, String dstAbsolutePath, boolean deleteSource, boolean overwrite); + + /** + * Move the resource from the source path to the destination path. + * + * @param srcLocalFileAbsolutePath the source local file + * @param dstAbsolutePath the destination path + * @param deleteSource whether to delete the source path after moving + * @param overwrite whether to overwrite the destination path if it exists + */ + void upload(String srcLocalFileAbsolutePath, String dstAbsolutePath, boolean deleteSource, boolean overwrite); + + /** + * Download the resource from the source path to the destination path. + * + * @param srcFileAbsolutePath the source path + * @param dstAbsoluteFile the destination file + * @param overwrite whether to overwrite the destination file if it exists + */ + void download(String srcFileAbsolutePath, String dstAbsoluteFile, boolean overwrite); + + /** + * Fetch the content of the file. + * + * @param fileAbsolutePath the file path + * @param skipLineNums the number of lines to skip + * @param limit the number of lines to read + * @return the content of the file + */ + List fetchFileContent(String fileAbsolutePath, int skipLineNums, int limit); + + /** + * Return the {@link StorageEntity} under the given path. + *

If the path is a file, return the file status. + *

If the path is a directory, return the file/directory under the directory. + *

If the path is not exist, will return empty. + * + * @param resourceAbsolutePath the resource absolute path, cannot be empty + */ + List listStorageEntity(String resourceAbsolutePath); + + /** + * Return the {@link StorageEntity} which is file under the given path + * + * @param resourceAbsolutePath the resource absolute path, cannot be empty + */ + List listFileStorageEntityRecursively(String resourceAbsolutePath); + + /** + * Return the {@link StorageEntity} under the current directory + * + * @param resourceAbsolutePath the resource absolute path, cannot be empty + */ + StorageEntity getStorageEntity(String resourceAbsolutePath); + +} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperateFactory.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperatorFactory.java similarity index 91% rename from dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperateFactory.java rename to dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperatorFactory.java index b3a60888c9..1e6e1f5a52 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperateFactory.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-api/src/main/java/org/apache/dolphinscheduler/plugin/storage/api/StorageOperatorFactory.java @@ -17,9 +17,9 @@ package org.apache.dolphinscheduler.plugin.storage.api; -public interface StorageOperateFactory { +public interface StorageOperatorFactory { - StorageOperate createStorageOperate(); + StorageOperator createStorageOperate(); StorageType getStorageOperate(); } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperator.java index e4176dc58e..00aa746e73 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperator.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperator.java @@ -18,18 +18,13 @@ package org.apache.dolphinscheduler.plugin.storage.gcs; import static org.apache.dolphinscheduler.common.constants.Constants.EMPTY_STRING; -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_FILE; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_UDF; import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; import org.apache.dolphinscheduler.common.utils.FileUtils; -import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.AbstractStorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.spi.enums.ResourceType; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.lang3.StringUtils; @@ -37,22 +32,24 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Date; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import lombok.Data; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import com.google.api.gax.paging.Page; @@ -64,82 +61,53 @@ import com.google.cloud.storage.Bucket; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; -@Data @Slf4j -public class GcsStorageOperator implements Closeable, StorageOperate { +public class GcsStorageOperator extends AbstractStorageOperator implements Closeable, StorageOperator { - private Storage gcsStorage; + private final Storage gcsStorage; - private String bucketName; + private final String bucketName; - private String credential; - - public GcsStorageOperator() { - - } - - public void init() { - try { - credential = readCredentials(); - bucketName = readBucketName(); - gcsStorage = buildGcsStorage(credential); - - checkBucketNameExists(bucketName); - } catch (IOException e) { - log.error("GCS Storage operator init failed", e); - } - } - - protected Storage buildGcsStorage(String credential) throws IOException { - return StorageOptions.newBuilder() + @SneakyThrows + public GcsStorageOperator(GcsStorageProperties gcsStorageProperties) { + super(gcsStorageProperties.getResourceUploadPath()); + bucketName = gcsStorageProperties.getBucketName(); + gcsStorage = StorageOptions.newBuilder() .setCredentials(ServiceAccountCredentials.fromStream( - Files.newInputStream(Paths.get(credential)))) + Files.newInputStream(Paths.get(gcsStorageProperties.getCredential())))) .build() .getService(); - } - protected String readCredentials() { - return PropertyUtils.getString(Constants.GOOGLE_CLOUD_STORAGE_CREDENTIAL); - } - - protected String readBucketName() { - return PropertyUtils.getString(Constants.GOOGLE_CLOUD_STORAGE_BUCKET_NAME); + checkBucketNameExists(bucketName); } @Override - public void createTenantDirIfNotExists(String tenantCode) throws Exception { - mkdir(tenantCode, getGcsResDir(tenantCode)); - mkdir(tenantCode, getGcsUdfDir(tenantCode)); - } - - @Override - public String getResDir(String tenantCode) { - return getGcsResDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public String getUdfDir(String tenantCode) { - return getGcsUdfDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public String getResourceFullName(String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName.replaceFirst(FOLDER_SEPARATOR, EMPTY_STRING); + public String getStorageBaseDirectory() { + // All directory should end with File.separator + if (resourceBaseAbsolutePath.startsWith("/")) { + log.warn("{} -> {} should not start with / in Gcs", Constants.RESOURCE_UPLOAD_PATH, + resourceBaseAbsolutePath); + return resourceBaseAbsolutePath.substring(1); } - return String.format(FORMAT_S_S, getGcsResDir(tenantCode), fileName); + return getStorageBaseDirectory(); } + @SneakyThrows @Override - public String getFileName(ResourceType resourceType, String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, EMPTY_STRING); + public void createStorageDir(String directoryAbsolutePath) { + directoryAbsolutePath = transformAbsolutePathToGcsKey(directoryAbsolutePath); + if (exists(directoryAbsolutePath)) { + throw new FileAlreadyExistsException("directory: " + directoryAbsolutePath + " already exists"); } - return getDir(resourceType, tenantCode) + fileName; + BlobInfo blobInfo = BlobInfo.newBuilder(BlobId.of(bucketName, directoryAbsolutePath)).build(); + gcsStorage.create(blobInfo, EMPTY_STRING.getBytes(StandardCharsets.UTF_8)); } + @SneakyThrows @Override - public void download(String srcFilePath, String dstFilePath, boolean overwrite) throws IOException { + public void download(String srcFilePath, String dstFilePath, boolean overwrite) { + srcFilePath = transformAbsolutePathToGcsKey(srcFilePath); + File dstFile = new File(dstFilePath); if (dstFile.isDirectory()) { Files.delete(dstFile.toPath()); @@ -152,40 +120,26 @@ public class GcsStorageOperator implements Closeable, StorageOperate { } @Override - public boolean exists(String fullName) throws IOException { - return isObjectExists(fullName); + public boolean exists(String fullName) { + fullName = transformAbsolutePathToGcsKey(fullName); + Blob blob = gcsStorage.get(BlobId.of(bucketName, fullName)); + return blob != null && blob.exists(); } + @SneakyThrows @Override - public boolean delete(String filePath, boolean recursive) throws IOException { - try { - if (isObjectExists(filePath)) { - gcsStorage.delete(BlobId.of(bucketName, filePath)); - } - return true; - } catch (Exception e) { - log.error("delete the object error,the resource path is {}", filePath); - return false; + public void delete(String filePath, boolean recursive) { + filePath = transformAbsolutePathToGcsKey(filePath); + if (exists(filePath)) { + gcsStorage.delete(BlobId.of(bucketName, filePath)); } } @Override - public boolean delete(String fullName, List childrenPathList, boolean recursive) throws IOException { - // append the resource fullName to the list for deletion. - childrenPathList.add(fullName); + public void copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) { + srcPath = transformGcsKeyToAbsolutePath(srcPath); + dstPath = transformGcsKeyToAbsolutePath(dstPath); - boolean result = true; - for (String filePath : childrenPathList) { - if (!delete(filePath, recursive)) { - result = false; - } - } - - return result; - } - - @Override - public boolean copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) throws IOException { BlobId source = BlobId.of(bucketName, srcPath); BlobId target = BlobId.of(bucketName, dstPath); @@ -198,31 +152,30 @@ public class GcsStorageOperator implements Closeable, StorageOperate { if (deleteSource) { gcsStorage.delete(source); } - return true; } + @SneakyThrows @Override - public boolean upload(String tenantCode, String srcFile, String dstPath, boolean deleteSource, - boolean overwrite) throws IOException { - try { - BlobInfo blobInfo = BlobInfo.newBuilder( - BlobId.of(bucketName, dstPath)).build(); + public void upload(String srcFile, String dstPath, boolean deleteSource, boolean overwrite) { + dstPath = transformAbsolutePathToGcsKey(dstPath); + if (exists(dstPath) && !overwrite) { + throw new FileAlreadyExistsException("file: " + dstPath + " already exists"); + } + BlobInfo blobInfo = BlobInfo.newBuilder( + BlobId.of(bucketName, dstPath)).build(); - Path srcPath = Paths.get(srcFile); - gcsStorage.create(blobInfo, Files.readAllBytes(srcPath)); + Path srcPath = Paths.get(srcFile); + gcsStorage.create(blobInfo, Files.readAllBytes(srcPath)); - if (deleteSource) { - Files.delete(srcPath); - } - return true; - } catch (Exception e) { - log.error("upload failed,the bucketName is {},the filePath is {}", bucketName, dstPath); - return false; + if (deleteSource) { + Files.delete(srcPath); } } + @SneakyThrows @Override - public List vimFile(String tenantCode, String filePath, int skipLineNums, int limit) throws IOException { + public List fetchFileContent(String filePath, int skipLineNums, int limit) { + filePath = transformAbsolutePathToGcsKey(filePath); if (StringUtils.isBlank(filePath)) { log.error("file path:{} is blank", filePath); return Collections.emptyList(); @@ -237,232 +190,58 @@ public class GcsStorageOperator implements Closeable, StorageOperate { } } - @Override - public void deleteTenant(String tenantCode) throws Exception { - deleteTenantCode(tenantCode); - } - - protected void deleteTenantCode(String tenantCode) { - deleteDirectory(getResDir(tenantCode)); - deleteDirectory(getUdfDir(tenantCode)); - } - - @Override - public String getDir(ResourceType resourceType, String tenantCode) { - switch (resourceType) { - case UDF: - return getUdfDir(tenantCode); - case FILE: - return getResDir(tenantCode); - case ALL: - return getGcsDataBasePath(); - default: - return EMPTY_STRING; - } - - } - - protected void deleteDirectory(String directoryName) { - if (isObjectExists(directoryName)) { - gcsStorage.delete(BlobId.of(bucketName, directoryName)); - } - } - - public String getGcsResDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_FILE, getGcsTenantDir(tenantCode)); - } - - public String getGcsUdfDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_UDF, getGcsTenantDir(tenantCode)); - } - - public String getGcsTenantDir(String tenantCode) { - return String.format(FORMAT_S_S, getGcsDataBasePath(), tenantCode); - } - - public String getGcsDataBasePath() { - if (FOLDER_SEPARATOR.equals(RESOURCE_UPLOAD_PATH)) { - return EMPTY_STRING; - } else { - return RESOURCE_UPLOAD_PATH.replaceFirst(FOLDER_SEPARATOR, EMPTY_STRING); - } - } - - @Override - public boolean mkdir(String tenantCode, String path) throws IOException { - String objectName = path + FOLDER_SEPARATOR; - if (!isObjectExists(objectName)) { - BlobInfo blobInfo = BlobInfo.newBuilder( - BlobId.of(bucketName, objectName)).build(); - - gcsStorage.create(blobInfo, EMPTY_STRING.getBytes(StandardCharsets.UTF_8)); - } - return true; - } - + @SneakyThrows @Override public void close() throws IOException { - try { - if (gcsStorage != null) { - gcsStorage.close(); - } - } catch (Exception e) { - throw new IOException(e); + if (gcsStorage != null) { + gcsStorage.close(); } } @Override - public ResUploadType returnStorageType() { - return ResUploadType.GCS; + public List listStorageEntity(String resourceAbsolutePath) { + resourceAbsolutePath = transformAbsolutePathToGcsKey(resourceAbsolutePath); + + Page blobs = gcsStorage.list(bucketName, Storage.BlobListOption.prefix(resourceAbsolutePath)); + List storageEntities = new ArrayList<>(); + blobs.iterateAll().forEach(blob -> storageEntities.add(transformBlobToStorageEntity(blob))); + return storageEntities; } @Override - public List listFilesStatusRecursively(String path, String defaultPath, String tenantCode, - ResourceType type) { + public List listFileStorageEntityRecursively(String resourceAbsolutePath) { + resourceAbsolutePath = transformAbsolutePathToGcsKey(resourceAbsolutePath); + + Set visited = new HashSet<>(); List storageEntityList = new ArrayList<>(); - LinkedList foldersToFetch = new LinkedList<>(); - - StorageEntity initialEntity = null; - try { - initialEntity = getFileStatus(path, defaultPath, tenantCode, type); - } catch (Exception e) { - log.error("error while listing files status recursively, path: {}", path, e); - return storageEntityList; - } - foldersToFetch.add(initialEntity); + LinkedList foldersToFetch = new LinkedList<>(); + foldersToFetch.addLast(resourceAbsolutePath); while (!foldersToFetch.isEmpty()) { - String pathToExplore = foldersToFetch.pop().getFullName(); - try { - List tempList = listFilesStatus(pathToExplore, defaultPath, tenantCode, type); - for (StorageEntity temp : tempList) { - if (temp.isDirectory()) { - foldersToFetch.add(temp); + String pathToExplore = foldersToFetch.pop(); + visited.add(pathToExplore); + List tempList = listStorageEntity(pathToExplore); + for (StorageEntity temp : tempList) { + if (temp.isDirectory()) { + if (visited.contains(temp.getFullName())) { + continue; } + foldersToFetch.add(temp.getFullName()); } - storageEntityList.addAll(tempList); - } catch (Exception e) { - log.error("error while listing files stat:wus recursively, path: {}", pathToExplore, e); } + storageEntityList.addAll(tempList); } - return storageEntityList; } @Override - public List listFilesStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception { - List storageEntityList = new ArrayList<>(); - - Page blobs; - try { - blobs = - gcsStorage.list( - bucketName, - Storage.BlobListOption.prefix(path), - Storage.BlobListOption.currentDirectory()); - } catch (Exception e) { - throw new RuntimeException("Get GCS file list exception. ", e); - } - - if (blobs == null) { - return storageEntityList; - } - - for (Blob blob : blobs.iterateAll()) { - if (path.equals(blob.getName())) { - continue; - } - if (blob.isDirectory()) { - String suffix = StringUtils.difference(path, blob.getName()); - String fileName = StringUtils.difference(defaultPath, blob.getName()); - StorageEntity entity = new StorageEntity(); - entity.setAlias(suffix); - entity.setFileName(fileName); - entity.setFullName(blob.getName()); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - entity.setCreateTime(null); - entity.setUpdateTime(null); - entity.setPfullName(path); - - storageEntityList.add(entity); - } else { - String[] aliasArr = blob.getName().split("/"); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, blob.getName()); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(blob.getName()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(blob.getSize()); - entity.setCreateTime(Date.from(blob.getCreateTimeOffsetDateTime().toInstant())); - entity.setUpdateTime(Date.from(blob.getUpdateTimeOffsetDateTime().toInstant())); - entity.setPfullName(path); - - storageEntityList.add(entity); - } - } - - return storageEntityList; + public StorageEntity getStorageEntity(String resourceAbsolutePath) { + resourceAbsolutePath = transformAbsolutePathToGcsKey(resourceAbsolutePath); + Blob blob = gcsStorage.get(BlobId.of(bucketName, resourceAbsolutePath)); + return transformBlobToStorageEntity(blob); } - @Override - public StorageEntity getFileStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception { - if (path.endsWith(FOLDER_SEPARATOR)) { - // the path is a directory that may or may not exist - String alias = findDirAlias(path); - String fileName = StringUtils.difference(defaultPath, path); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(path); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - - return entity; - } else { - if (isObjectExists(path)) { - Blob blob = gcsStorage.get(BlobId.of(bucketName, path)); - - String[] aliasArr = blob.getName().split(FOLDER_SEPARATOR); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, blob.getName()); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(blob.getName()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(blob.getSize()); - entity.setCreateTime(Date.from(blob.getCreateTimeOffsetDateTime().toInstant())); - entity.setUpdateTime(Date.from(blob.getUpdateTimeOffsetDateTime().toInstant())); - - return entity; - } else { - throw new FileNotFoundException("Object is not found in GCS Bucket: " + bucketName); - } - } - } - - protected boolean isObjectExists(String objectName) { - Blob blob = gcsStorage.get(BlobId.of(bucketName, objectName)); - return blob != null && blob.exists(); - } - - public void checkBucketNameExists(String bucketName) { + private void checkBucketNameExists(String bucketName) { if (StringUtils.isBlank(bucketName)) { throw new IllegalArgumentException(Constants.GOOGLE_CLOUD_STORAGE_BUCKET_NAME + " is blank"); } @@ -483,12 +262,35 @@ public class GcsStorageOperator implements Closeable, StorageOperate { } } - private String findDirAlias(String dirPath) { - if (!dirPath.endsWith(FOLDER_SEPARATOR)) { - return dirPath; - } + private StorageEntity transformBlobToStorageEntity(Blob blob) { + String absolutePath = transformGcsKeyToAbsolutePath(blob.getName()); - Path path = Paths.get(dirPath); - return path.getName(path.getNameCount() - 1) + FOLDER_SEPARATOR; + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + + StorageEntity entity = new StorageEntity(); + entity.setFileName(new File(absolutePath).getName()); + entity.setFullName(absolutePath); + entity.setDirectory(resourceMetaData.isDirectory()); + entity.setType(resourceMetaData.getResourceType()); + entity.setSize(blob.getSize()); + entity.setCreateTime(Date.from(blob.getCreateTimeOffsetDateTime().toInstant())); + entity.setUpdateTime(Date.from(blob.getUpdateTimeOffsetDateTime().toInstant())); + return entity; } + + private String transformAbsolutePathToGcsKey(String absolutePath) { + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + if (resourceMetaData.isDirectory()) { + return FileUtils.concatFilePath(absolutePath, "/"); + } + return absolutePath; + } + + private String transformGcsKeyToAbsolutePath(String gcsKey) { + if (gcsKey.endsWith("/")) { + return gcsKey.substring(0, gcsKey.length() - 1); + } + return gcsKey; + } + } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperatorFactory.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperatorFactory.java index eedd41c01f..2bc1a4bfcb 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperatorFactory.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperatorFactory.java @@ -17,20 +17,29 @@ package org.apache.dolphinscheduler.plugin.storage.gcs; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperateFactory; +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperatorFactory; import org.apache.dolphinscheduler.plugin.storage.api.StorageType; import com.google.auto.service.AutoService; -@AutoService(StorageOperateFactory.class) -public class GcsStorageOperatorFactory implements StorageOperateFactory { +@AutoService(StorageOperatorFactory.class) +public class GcsStorageOperatorFactory implements StorageOperatorFactory { @Override - public StorageOperate createStorageOperate() { - GcsStorageOperator gcsStorageOperator = new GcsStorageOperator(); - gcsStorageOperator.init(); - return gcsStorageOperator; + public StorageOperator createStorageOperate() { + final GcsStorageProperties gcsStorageProperties = getGcsStorageProperties(); + return new GcsStorageOperator(gcsStorageProperties); + } + + public GcsStorageProperties getGcsStorageProperties() { + return GcsStorageProperties.builder() + .resourceUploadPath(PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler")) + .credential(PropertyUtils.getString(Constants.GOOGLE_CLOUD_STORAGE_CREDENTIAL)) + .bucketName(PropertyUtils.getString(Constants.GOOGLE_CLOUD_STORAGE_BUCKET_NAME)) + .build(); } @Override diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageProperties.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageProperties.java new file mode 100644 index 0000000000..5fd1b4b747 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/main/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageProperties.java @@ -0,0 +1,36 @@ +/* + * 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.dolphinscheduler.plugin.storage.gcs; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GcsStorageProperties { + + private String bucketName; + + private String credential; + + private String resourceUploadPath; +} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/test/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperatorTest.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/test/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperatorTest.java deleted file mode 100644 index eecde956d7..0000000000 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-gcs/src/test/java/org/apache/dolphinscheduler/plugin/storage/gcs/GcsStorageOperatorTest.java +++ /dev/null @@ -1,290 +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.dolphinscheduler.plugin.storage.gcs; - -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.spi.enums.ResourceType; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.google.cloud.storage.BlobId; -import com.google.cloud.storage.BlobInfo; -import com.google.cloud.storage.Storage; - -@ExtendWith(MockitoExtension.class) -public class GcsStorageOperatorTest { - - private static final String CREDENTIAL_MOCK = "CREDENTIAL_MOCK"; - - private static final String BUCKET_NAME_MOCK = "BUCKET_NAME_MOCK"; - - private static final String TENANT_CODE_MOCK = "TENANT_CODE_MOCK"; - - private static final String DIR_MOCK = "DIR_MOCK"; - - private static final String FILE_NAME_MOCK = "FILE_NAME_MOCK"; - - private static final String FILE_PATH_MOCK = "FILE_PATH_MOCK"; - - private static final String FULL_NAME = "/tmp/dir1/"; - - private static final String DEFAULT_PATH = "/tmp/"; - - @Mock - private Storage gcsStorage; - - private GcsStorageOperator gcsStorageOperator; - - @BeforeEach - public void setUp() throws Exception { - gcsStorageOperator = Mockito.spy(GcsStorageOperator.class); - Mockito.doReturn(CREDENTIAL_MOCK).when(gcsStorageOperator).readCredentials(); - Mockito.doReturn(BUCKET_NAME_MOCK).when(gcsStorageOperator).readBucketName(); - Mockito.doReturn(gcsStorage).when(gcsStorageOperator).buildGcsStorage(Mockito.anyString()); - Mockito.doNothing().when(gcsStorageOperator).checkBucketNameExists(Mockito.anyString()); - - gcsStorageOperator.init(); - } - - @Test - public void testInit() throws Exception { - verify(gcsStorageOperator, times(1)).buildGcsStorage(CREDENTIAL_MOCK); - Assertions.assertEquals(CREDENTIAL_MOCK, gcsStorageOperator.getCredential()); - Assertions.assertEquals(BUCKET_NAME_MOCK, gcsStorageOperator.getBucketName()); - } - - @Test - public void testClose() throws Exception { - doNothing().when(gcsStorage).close(); - gcsStorageOperator.close(); - verify(gcsStorage, times(1)).close(); - } - - @Test - public void createTenantResAndUdfDir() throws Exception { - doReturn(DIR_MOCK).when(gcsStorageOperator).getGcsResDir(TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(gcsStorageOperator).getGcsUdfDir(TENANT_CODE_MOCK); - doReturn(true).when(gcsStorageOperator).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - gcsStorageOperator.createTenantDirIfNotExists(TENANT_CODE_MOCK); - verify(gcsStorageOperator, times(2)).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - } - - @Test - public void getResDir() { - final String expectedResourceDir = String.format("dolphinscheduler/%s/resources/", TENANT_CODE_MOCK); - final String dir = gcsStorageOperator.getResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedResourceDir, dir); - } - - @Test - public void getUdfDir() { - final String expectedUdfDir = String.format("dolphinscheduler/%s/udfs/", TENANT_CODE_MOCK); - final String dir = gcsStorageOperator.getUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedUdfDir, dir); - } - - @Test - public void mkdirWhenDirExists() { - boolean isSuccess = false; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - Mockito.doReturn(true).when(gcsStorageOperator).isObjectExists(key); - isSuccess = gcsStorageOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); - } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void mkdirWhenDirNotExists() { - boolean isSuccess = true; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - doReturn(false).when(gcsStorageOperator).isObjectExists(key); - isSuccess = gcsStorageOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - verify(gcsStorage, times(1)).create(Mockito.any(BlobInfo.class), Mockito.any(byte[].class)); - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); - } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void getResourceFullName() { - final String expectedResourceFullName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFullName = gcsStorageOperator.getResourceFullName(TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedResourceFullName, resourceFullName); - } - - @Test - public void getResourceFileName() { - final String expectedResourceFileName = FILE_NAME_MOCK; - final String resourceFullName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFileName = gcsStorageOperator.getResourceFileName(TENANT_CODE_MOCK, resourceFullName); - Assertions.assertEquals(expectedResourceFileName, resourceFileName); - } - - @Test - public void getFileName() { - final String expectedFileName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String fileName = gcsStorageOperator.getFileName(ResourceType.FILE, TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedFileName, fileName); - } - - @Test - public void exists() { - boolean doesExist = false; - doReturn(true).when(gcsStorageOperator).isObjectExists(FILE_NAME_MOCK); - try { - doesExist = gcsStorageOperator.exists(FILE_NAME_MOCK); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(doesExist); - } - - @Test - public void delete() { - boolean isDeleted = false; - doReturn(true).when(gcsStorage).delete(Mockito.any(BlobId.class)); - doReturn(true).when(gcsStorageOperator).isObjectExists(FILE_NAME_MOCK); - try { - isDeleted = gcsStorageOperator.delete(FILE_NAME_MOCK, true); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isDeleted); - verify(gcsStorage, times(1)).delete(Mockito.any(BlobId.class)); - } - - @Test - public void copy() { - boolean isSuccess = false; - doReturn(null).when(gcsStorage).copy(Mockito.any()); - try { - isSuccess = gcsStorageOperator.copy(FILE_PATH_MOCK, FILE_PATH_MOCK, false, false); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isSuccess); - verify(gcsStorage, times(1)).copy(Mockito.any()); - } - - @Test - public void deleteTenant() { - doNothing().when(gcsStorageOperator).deleteTenantCode(anyString()); - try { - gcsStorageOperator.deleteTenant(TENANT_CODE_MOCK); - } catch (Exception e) { - Assertions.fail("unexpected exception caught in unit test"); - } - - verify(gcsStorageOperator, times(1)).deleteTenantCode(anyString()); - } - - @Test - public void getGcsResDir() { - final String expectedGcsResDir = String.format("dolphinscheduler/%s/resources", TENANT_CODE_MOCK); - final String gcsResDir = gcsStorageOperator.getGcsResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedGcsResDir, gcsResDir); - } - - @Test - public void getGcsUdfDir() { - final String expectedGcsUdfDir = String.format("dolphinscheduler/%s/udfs", TENANT_CODE_MOCK); - final String gcsUdfDir = gcsStorageOperator.getGcsUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedGcsUdfDir, gcsUdfDir); - } - - @Test - public void getGcsTenantDir() { - final String expectedGcsTenantDir = String.format(FORMAT_S_S, DIR_MOCK, TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(gcsStorageOperator).getGcsDataBasePath(); - final String gcsTenantDir = gcsStorageOperator.getGcsTenantDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedGcsTenantDir, gcsTenantDir); - } - - @Test - public void deleteDir() { - doReturn(true).when(gcsStorageOperator).isObjectExists(Mockito.any()); - gcsStorageOperator.deleteDirectory(DIR_MOCK); - verify(gcsStorage, times(1)).delete(Mockito.any(BlobId.class)); - } - - @Test - public void testGetFileStatus() throws Exception { - StorageEntity entity = - gcsStorageOperator.getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(FULL_NAME, entity.getFullName()); - Assertions.assertEquals("dir1/", entity.getFileName()); - } - - @Test - public void testListFilesStatus() throws Exception { - Mockito.doReturn(null).when(gcsStorage).list(Mockito.any(), Mockito.any(Storage.BlobListOption.class), - Mockito.any(Storage.BlobListOption.class)); - List result = - gcsStorageOperator.listFilesStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - verify(gcsStorage, times(1)).list(Mockito.any(), Mockito.any(Storage.BlobListOption.class), - Mockito.any(Storage.BlobListOption.class)); - } - - @Test - public void testListFilesStatusRecursively() throws Exception { - StorageEntity entity = new StorageEntity(); - entity.setFullName(FULL_NAME); - - doReturn(entity).when(gcsStorageOperator).getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, - ResourceType.FILE); - doReturn(Collections.EMPTY_LIST).when(gcsStorageOperator).listFilesStatus(anyString(), anyString(), anyString(), - Mockito.any(ResourceType.class)); - - List result = - gcsStorageOperator.listFilesStatusRecursively(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, - ResourceType.FILE); - Assertions.assertEquals(0, result.size()); - } -} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/pom.xml b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/pom.xml index 2a07584948..04e06df244 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/pom.xml +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/pom.xml @@ -228,5 +228,11 @@ + + + org.testcontainers + testcontainers + test + diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperator.java index 4479c93a3d..e9a95eee11 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperator.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperator.java @@ -17,340 +17,145 @@ package org.apache.dolphinscheduler.plugin.storage.hdfs; -import static org.apache.dolphinscheduler.common.constants.Constants.EMPTY_STRING; -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_FILE; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_UDF; - import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; -import org.apache.dolphinscheduler.common.exception.BaseException; -import org.apache.dolphinscheduler.common.utils.HttpUtils; -import org.apache.dolphinscheduler.common.utils.JSONUtils; -import org.apache.dolphinscheduler.common.utils.KerberosHttpClient; -import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.common.utils.FileUtils; import org.apache.dolphinscheduler.plugin.datasource.api.utils.CommonUtils; +import org.apache.dolphinscheduler.plugin.storage.api.AbstractStorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.spi.enums.ResourceType; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; -import org.apache.commons.io.IOUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileUtil; +import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.RemoteIterator; import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.security.UserGroupInformation; import java.io.BufferedReader; import java.io.Closeable; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import com.fasterxml.jackson.databind.node.ObjectNode; - @Slf4j -public class HdfsStorageOperator implements Closeable, StorageOperate { +public class HdfsStorageOperator extends AbstractStorageOperator implements Closeable, StorageOperator { - protected static HdfsStorageProperties hdfsProperties = new HdfsStorageProperties(); - private static final String HADOOP_UTILS_KEY = "HADOOP_UTILS_KEY"; - - private volatile boolean yarnEnabled = false; + private final HdfsStorageProperties hdfsProperties; private Configuration configuration; private FileSystem fs; - public HdfsStorageOperator() { - this(new HdfsStorageProperties()); - } - public HdfsStorageOperator(HdfsStorageProperties hdfsStorageProperties) { + super(hdfsStorageProperties.getResourceUploadPath()); // Overwrite config from passing hdfsStorageProperties hdfsProperties = hdfsStorageProperties; init(); initHdfsPath(); } - /** - * init dolphinscheduler root path in hdfs - */ - + @SneakyThrows private void initHdfsPath() { - Path path = new Path(RESOURCE_UPLOAD_PATH); - try { - if (!fs.exists(path)) { - fs.mkdirs(path); + Path path = new Path(resourceBaseAbsolutePath); + if (!fs.exists(path)) { + if (!fs.mkdirs(path)) { + log.info("Create hdfs path: {} failed", path); + } else { + log.error("Create hdfs path: {} success", path); } - } catch (Exception e) { - log.error(e.getMessage(), e); } } - /** - * init hadoop configuration - */ - public void init() throws NullPointerException { - try { - configuration = new HdfsConfiguration(); + @SneakyThrows + private void init() { + configuration = new HdfsConfiguration(); - String hdfsUser = hdfsProperties.getUser(); - if (CommonUtils.loadKerberosConf(configuration)) { - hdfsUser = ""; - } - - String defaultFS = getDefaultFS(); - // first get key from core-site.xml hdfs-site.xml ,if null ,then try to get from properties file - // the default is the local file system - if (StringUtils.isNotBlank(defaultFS)) { - Map fsRelatedProps = PropertyUtils.getByPrefix("fs."); - configuration.set(Constants.HDFS_DEFAULT_FS, defaultFS); - fsRelatedProps.forEach((key, value) -> configuration.set(key, value)); - } else { - log.error("property:{} can not to be empty, please set!", Constants.FS_DEFAULT_FS); - throw new NullPointerException( - String.format("property: %s can not to be empty, please set!", Constants.FS_DEFAULT_FS)); - } - - if (!defaultFS.startsWith("file")) { - log.info("get property:{} -> {}, from core-site.xml hdfs-site.xml ", Constants.FS_DEFAULT_FS, - defaultFS); - } - - if (StringUtils.isNotEmpty(hdfsUser)) { - UserGroupInformation ugi = UserGroupInformation.createRemoteUser(hdfsUser); - ugi.doAs((PrivilegedExceptionAction) () -> { - fs = FileSystem.get(configuration); - return true; - }); - } else { - log.warn("resource.hdfs.root.user is not set value!"); - fs = FileSystem.get(configuration); - } - - } catch (Exception e) { - log.error(e.getMessage(), e); + if (MapUtils.isNotEmpty(hdfsProperties.getConfigurationProperties())) { + hdfsProperties.getConfigurationProperties().forEach((key, value) -> { + configuration.set(key, value); + log.info("Set HDFS prop: {} -> {}", key, value); + }); } - } - /** - * @return Configuration - */ - public Configuration getConfiguration() { - return configuration; - } - - /** - * @return DefaultFS - */ - public String getDefaultFS() { String defaultFS = hdfsProperties.getDefaultFS(); - if (StringUtils.isBlank(defaultFS)) { - defaultFS = getConfiguration().get(Constants.HDFS_DEFAULT_FS); + if (StringUtils.isNotEmpty(defaultFS)) { + configuration.set(Constants.HDFS_DEFAULT_FS, hdfsProperties.getDefaultFS()); } - return defaultFS; + + if (CommonUtils.getKerberosStartupState()) { + CommonUtils.loadKerberosConf(configuration); + fs = FileSystem.get(configuration); + log.info("Initialize HdfsStorageOperator with kerberos"); + return; + } + if (StringUtils.isNotEmpty(hdfsProperties.getUser())) { + UserGroupInformation ugi = UserGroupInformation.createRemoteUser(hdfsProperties.getUser()); + ugi.doAs((PrivilegedExceptionAction) () -> { + fs = FileSystem.get(configuration); + return true; + }); + UserGroupInformation.setLoginUser(ugi); + log.info("Initialize HdfsStorageOperator with remote user: {}", hdfsProperties.getUser()); + return; + } + fs = FileSystem.get(configuration); + log.info("Initialize HdfsStorageOperator with default user"); + } - /** - * get application url - * if rmHaIds contains xx, it signs not use resourcemanager - * otherwise: - * if rmHaIds is empty, single resourcemanager enabled - * if rmHaIds not empty: resourcemanager HA enabled - * - * @param applicationId application id - * @return url of application - */ - public String getApplicationUrl(String applicationId) throws BaseException { - - yarnEnabled = true; - String appUrl = StringUtils.isEmpty(hdfsProperties.getYarnResourceRmIds()) - ? hdfsProperties.getYarnAppStatusAddress() - : getAppAddress(hdfsProperties.getYarnAppStatusAddress(), hdfsProperties.getYarnResourceRmIds()); - if (StringUtils.isBlank(appUrl)) { - throw new BaseException("yarn application url generation failed"); - } - log.debug("yarn application url:{}, applicationId:{}", appUrl, applicationId); - return String.format(appUrl, hdfsProperties.getHadoopResourceManagerHttpAddressPort(), applicationId); + @Override + public String getStorageBaseDirectory() { + String defaultFS = hdfsProperties.getDefaultFS(); + return FileUtils.concatFilePath(defaultFS, resourceBaseAbsolutePath); } - public String getJobHistoryUrl(String applicationId) { - // eg:application_1587475402360_712719 -> job_1587475402360_712719 - String jobId = applicationId.replace("application", "job"); - return String.format(hdfsProperties.getYarnJobHistoryStatusAddress(), jobId); - } - - /** - * cat file on hdfs - * - * @param hdfsFilePath hdfs file path - * @return byte[] byte array - * @throws IOException errors - */ - public byte[] catFile(String hdfsFilePath) throws IOException { - - if (StringUtils.isBlank(hdfsFilePath)) { - log.error("hdfs file path:{} is blank", hdfsFilePath); - return new byte[0]; - } - - try (FSDataInputStream fsDataInputStream = fs.open(new Path(hdfsFilePath))) { - return IOUtils.toByteArray(fsDataInputStream); - } - } - - /** - * cat file on hdfs - * - * @param hdfsFilePath hdfs file path - * @param skipLineNums skip line numbers - * @param limit read how many lines - * @return content of file - * @throws IOException errors - */ - public List catFile(String hdfsFilePath, int skipLineNums, int limit) throws IOException { - - if (StringUtils.isBlank(hdfsFilePath)) { - log.error("hdfs file path:{} is blank", hdfsFilePath); - return Collections.emptyList(); - } - - try (FSDataInputStream in = fs.open(new Path(hdfsFilePath))) { - BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); - Stream stream = br.lines().skip(skipLineNums).limit(limit); - return stream.collect(Collectors.toList()); + @SneakyThrows + @Override + public List fetchFileContent(String hdfsFilePath, int skipLineNums, int limit) { + try ( + FSDataInputStream in = fs.open(new Path(hdfsFilePath)); + InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.UTF_8); + BufferedReader br = new BufferedReader(inputStreamReader)) { + return br.lines() + .skip(skipLineNums) + .limit(limit) + .collect(Collectors.toList()); } } @Override - public List vimFile(String bucketName, String hdfsFilePath, int skipLineNums, - int limit) throws IOException { - return catFile(hdfsFilePath, skipLineNums, limit); + @SneakyThrows + public void createStorageDir(String directoryAbsolutePath) { + Path path = new Path(directoryAbsolutePath); + if (fs.exists(path)) { + throw new FileAlreadyExistsException("Directory already exists: " + directoryAbsolutePath); + } + fs.mkdirs(new Path(directoryAbsolutePath)); } + @SneakyThrows @Override - public void createTenantDirIfNotExists(String tenantCode) throws IOException { - mkdir(tenantCode, getHdfsResDir(tenantCode)); - mkdir(tenantCode, getHdfsUdfDir(tenantCode)); - } - - @Override - public String getResDir(String tenantCode) { - return getHdfsResDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public String getUdfDir(String tenantCode) { - return getHdfsUdfDir(tenantCode) + FOLDER_SEPARATOR; - } - - /** - * make the given file and all non-existent parents into - * directories. Has the semantics of Unix 'mkdir -p'. - * Existence of the directory hierarchy is not an error. - * - * @param hdfsPath path to create - * @return mkdir result - * @throws IOException errors - */ - @Override - public boolean mkdir(String tenantCode, String hdfsPath) throws IOException { - return fs.mkdirs(new Path(addFolderSeparatorIfNotExisted(hdfsPath))); - } - - @Override - public String getResourceFullName(String tenantCode, String fullName) { - return getHdfsResourceFileName(tenantCode, fullName); - } - - @Override - public String getFileName(ResourceType resourceType, String tenantCode, String fileName) { - return getHdfsFileName(resourceType, tenantCode, fileName); - } - - @Override - public void download(String srcHdfsFilePath, String dstFile, boolean overwrite) throws IOException { - copyHdfsToLocal(srcHdfsFilePath, dstFile, false, overwrite); - } - - /** - * copy files between FileSystems - * - * @param srcPath source hdfs path - * @param dstPath destination hdfs path - * @param deleteSource whether to delete the src - * @param overwrite whether to overwrite an existing file - * @return if success or not - * @throws IOException errors - */ - @Override - public boolean copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) throws IOException { - return FileUtil.copy(fs, new Path(srcPath), fs, new Path(dstPath), deleteSource, overwrite, fs.getConf()); - } - - /** - * the src file is on the local disk. Add it to FS at - * the given dst name. - * - * @param srcFile local file - * @param dstHdfsPath destination hdfs path - * @param deleteSource whether to delete the src - * @param overwrite whether to overwrite an existing file - * @return if success or not - * @throws IOException errors - */ - public boolean copyLocalToHdfs(String srcFile, String dstHdfsPath, boolean deleteSource, - boolean overwrite) throws IOException { - Path srcPath = new Path(srcFile); - Path dstPath = new Path(dstHdfsPath); - - fs.copyFromLocalFile(deleteSource, overwrite, srcPath, dstPath); - - return true; - } - - @Override - public boolean upload(String buckName, String srcFile, String dstPath, boolean deleteSource, - boolean overwrite) throws IOException { - return copyLocalToHdfs(srcFile, dstPath, deleteSource, overwrite); - } - - /** - * copy hdfs file to local - * - * @param srcHdfsFilePath source hdfs file path - * - * @param dstFile destination file - * - * @param deleteSource delete source - * - * @param overwrite overwrite - * - * @return result of copy hdfs file to local - * - * @throws IOException errors - */ - public boolean copyHdfsToLocal(String srcHdfsFilePath, String dstFile, boolean deleteSource, - boolean overwrite) throws IOException { - + public void download(String srcHdfsFilePath, String dstFile, boolean overwrite) { Path srcPath = new Path(srcHdfsFilePath); File dstPath = new File(dstFile); @@ -365,336 +170,83 @@ public class HdfsStorageOperator implements Closeable, StorageOperate { } if (!dstPath.getParentFile().exists() && !dstPath.getParentFile().mkdirs()) { - return false; + throw new IOException("Failed to create parent directory for destination file"); } - return FileUtil.copy(fs, srcPath, dstPath, deleteSource, fs.getConf()); + FileUtil.copy(fs, srcPath, dstPath, false, fs.getConf()); } - /** - * delete a file - * - * @param hdfsFilePath the path to delete. - * @param recursive if path is a directory and set to - * true, the directory is deleted else throws an exception. In - * case of a file the recursive can be set to either true or false. - * @return true if delete is successful else false. - * @throws IOException errors - */ + @SneakyThrows @Override - public boolean delete(String hdfsFilePath, boolean recursive) throws IOException { - return fs.delete(new Path(hdfsFilePath), recursive); + public void copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) { + FileUtil.copy(fs, new Path(srcPath), fs, new Path(dstPath), deleteSource, overwrite, fs.getConf()); } - /** - * delete a list of files - * - * @param filePath the path to delete, usually it is a directory. - * @param recursive if path is a directory and set to - * true, the directory is deleted else throws an exception. In - * case of a file the recursive can be set to either true or false. - * @return true if delete is successful else false. - * @throws IOException errors - */ - + @SneakyThrows @Override - public boolean delete(String filePath, List childrenPathArray, boolean recursive) throws IOException { - if (filePath.endsWith("/")) { - return fs.delete(new Path(filePath), true); + public void upload(String srcAbsoluteFilePath, + String dstAbsoluteFilePath, + boolean deleteSource, + boolean overwrite) { + Path srcPath = new Path(srcAbsoluteFilePath); + Path dstPath = new Path(dstAbsoluteFilePath); + fs.copyFromLocalFile(deleteSource, overwrite, srcPath, dstPath); + } + + @SneakyThrows + @Override + public void delete(String resourceAbsolutePath, boolean recursive) { + exceptionIfPathEmpty(resourceAbsolutePath); + fs.delete(new Path(resourceAbsolutePath), recursive); + } + + @SneakyThrows + @Override + public boolean exists(String resourceAbsolutePath) { + exceptionIfPathEmpty(resourceAbsolutePath); + return fs.exists(new Path(resourceAbsolutePath)); + } + + @SneakyThrows + @Override + public List listStorageEntity(String resourceAbsolutePath) { + exceptionIfPathEmpty(resourceAbsolutePath); + Path path = new Path(resourceAbsolutePath); + if (!fs.exists(path)) { + return Collections.emptyList(); } - return fs.delete(new Path(filePath), recursive); + return Arrays.stream(fs.listStatus(new Path(resourceAbsolutePath))) + .map(this::transformFileStatusToResourceMetadata) + .collect(Collectors.toList()); } - /** - * check if exists - * - * @param hdfsFilePath source file path - * @return result of exists or not - * @throws IOException errors - */ + @SneakyThrows @Override - public boolean exists(String hdfsFilePath) throws IOException { - return fs.exists(new Path(hdfsFilePath)); - } + public List listFileStorageEntityRecursively(String resourceAbsolutePath) { + exceptionIfPathEmpty(resourceAbsolutePath); - /** - * Gets a list of files in the directory - * - * @param path file fullName path - * @return {@link FileStatus} file status - * @throws IOException errors - */ - @Override - public List listFilesStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws IOException { - // TODO: Does listStatus truncate resultList if its size goes above certain threshold (like a 1000 in S3) - // TODO: add hdfs prefix getFile - List storageEntityList = new ArrayList<>(); - try { - Path filePath = new Path(path); - if (!fs.exists(filePath)) { - return storageEntityList; + List result = new ArrayList<>(); + + LinkedList foldersToFetch = new LinkedList<>(); + foldersToFetch.addLast(resourceAbsolutePath); + + while (!foldersToFetch.isEmpty()) { + String absolutePath = foldersToFetch.pollFirst(); + RemoteIterator remoteIterator = fs.listFiles(new Path(absolutePath), true); + while (remoteIterator.hasNext()) { + LocatedFileStatus locatedFileStatus = remoteIterator.next(); + result.add(transformFileStatusToResourceMetadata(locatedFileStatus)); } - FileStatus[] fileStatuses = fs.listStatus(filePath); - - // transform FileStatusArray into the StorageEntity List - for (FileStatus fileStatus : fileStatuses) { - if (fileStatus.isDirectory()) { - // the path is a directory - String fullName = fileStatus.getPath().toString(); - fullName = addFolderSeparatorIfNotExisted(fullName); - - String suffix = StringUtils.difference(path, fullName); - String fileName = StringUtils.difference(defaultPath, fullName); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(suffix); - entity.setFileName(fileName); - entity.setFullName(fullName); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(fileStatus.getLen()); - entity.setCreateTime(new Date(fileStatus.getModificationTime())); - entity.setUpdateTime(new Date(fileStatus.getModificationTime())); - entity.setPfullName(path); - - storageEntityList.add(entity); - } else { - // the path is a file - String fullName = fileStatus.getPath().toString(); - String[] aliasArr = fullName.split("/"); - String alias = aliasArr[aliasArr.length - 1]; - - String fileName = StringUtils.difference(defaultPath, fullName); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(fullName); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(fileStatus.getLen()); - entity.setCreateTime(new Date(fileStatus.getModificationTime())); - entity.setUpdateTime(new Date(fileStatus.getModificationTime())); - entity.setPfullName(path); - - storageEntityList.add(entity); - } - } - } catch (FileNotFoundException e) { - throw new FileNotFoundException("The path does not exist."); - } catch (IOException e) { - throw new IOException("Get file list exception.", e); } - - return storageEntityList; + return result; } + @SneakyThrows @Override - public StorageEntity getFileStatus(String path, String prefix, String tenantCode, - ResourceType type) throws IOException { - try { - FileStatus fileStatus = fs.getFileStatus(new Path(path)); - String alias = ""; - String fileName = ""; - String fullName = fileStatus.getPath().toString(); - if (fileStatus.isDirectory()) { - fullName = addFolderSeparatorIfNotExisted(fullName); - alias = findDirAlias(fullName); - fileName = StringUtils.difference(prefix, fullName); - } else { - String[] aliasArr = fileStatus.getPath().toString().split("/"); - alias = aliasArr[aliasArr.length - 1]; - fileName = StringUtils.difference(prefix, fileStatus.getPath().toString()); - } - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(fullName); - entity.setDirectory(fileStatus.isDirectory()); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(fileStatus.getLen()); - entity.setCreateTime(new Date(fileStatus.getModificationTime())); - entity.setUpdateTime(new Date(fileStatus.getModificationTime())); - entity.setPfullName(path); - - return entity; - } catch (FileNotFoundException e) { - throw new FileNotFoundException("The path does not exist."); - } catch (IOException e) { - throw new IOException("Get file exception.", e); - } - } - - /** - * Renames Path src to Path dst. Can take place on local fs - * or remote DFS. - * - * @param src path to be renamed - * @param dst new path after rename - * @return true if rename is successful - * @throws IOException on failure - */ - public boolean rename(String src, String dst) throws IOException { - return fs.rename(new Path(src), new Path(dst)); - } - - /** - * hadoop resourcemanager enabled or not - * - * @return result - */ - public boolean isYarnEnabled() { - return yarnEnabled; - } - - /** - * get data hdfs path - * - * @return data hdfs path - */ - public static String getHdfsDataBasePath() { - String defaultFS = hdfsProperties.getDefaultFS(); - defaultFS = defaultFS.endsWith("/") ? StringUtils.chop(defaultFS) : defaultFS; - if (FOLDER_SEPARATOR.equals(RESOURCE_UPLOAD_PATH)) { - return defaultFS + ""; - } else { - return defaultFS + RESOURCE_UPLOAD_PATH; - } - } - - /** - * hdfs resource dir - * - * @param tenantCode tenant code - * @param resourceType resource type - * @return hdfs resource dir - */ - public static String getHdfsDir(ResourceType resourceType, String tenantCode) { - switch (resourceType) { - case UDF: - return getHdfsUdfDir(tenantCode); - case FILE: - return getHdfsResDir(tenantCode); - case ALL: - return getHdfsDataBasePath(); - default: - return EMPTY_STRING; - } - } - - @Override - public String getDir(ResourceType resourceType, String tenantCode) { - return getHdfsDir(resourceType, tenantCode); - } - - /** - * hdfs resource dir - * - * @param tenantCode tenant code - * @return hdfs resource dir - */ - public static String getHdfsResDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_FILE, getHdfsTenantDir(tenantCode)); - } - - /** - * hdfs udf dir - * - * @param tenantCode tenant code - * @return get udf dir on hdfs - */ - public static String getHdfsUdfDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_UDF, getHdfsTenantDir(tenantCode)); - } - - /** - * get hdfs file name - * - * @param resourceType resource type - * @param tenantCode tenant code - * @param fileName file name - * @return hdfs file name - */ - public static String getHdfsFileName(ResourceType resourceType, String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return String.format(FORMAT_S_S, getHdfsDir(resourceType, tenantCode), fileName); - } - - /** - * get absolute path and name for resource file on hdfs - * - * @param tenantCode tenant code - * @param fileName file name - * @return get absolute path and name for file on hdfs - */ - public static String getHdfsResourceFileName(String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return String.format(FORMAT_S_S, getHdfsResDir(tenantCode), fileName); - } - - /** - * get absolute path and name for udf file on hdfs - * - * @param tenantCode tenant code - * @param fileName file name - * @return get absolute path and name for udf file on hdfs - */ - public static String getHdfsUdfFileName(String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return String.format(FORMAT_S_S, getHdfsUdfDir(tenantCode), fileName); - } - - /** - * @param tenantCode tenant code - * @return file directory of tenants on hdfs - */ - public static String getHdfsTenantDir(String tenantCode) { - return String.format(FORMAT_S_S, getHdfsDataBasePath(), tenantCode); - } - - /** - * getAppAddress - * - * @param appAddress app address - * @param rmHa resource manager ha - * @return app address - */ - public static String getAppAddress(String appAddress, String rmHa) { - - String[] split1 = appAddress.split(Constants.DOUBLE_SLASH); - - if (split1.length != 2) { - return null; - } - - String start = split1[0] + Constants.DOUBLE_SLASH; - String[] split2 = split1[1].split(Constants.COLON); - - if (split2.length != 2) { - return null; - } - - String end = Constants.COLON + split2[1]; - - // get active ResourceManager - String activeRM = YarnHAAdminUtils.getActiveRMName(start, rmHa); - - if (StringUtils.isEmpty(activeRM)) { - return null; - } - - return start + activeRM + end; + public StorageEntity getStorageEntity(String resourceAbsolutePath) { + exceptionIfPathEmpty(resourceAbsolutePath); + FileStatus fileStatus = fs.getFileStatus(new Path(resourceAbsolutePath)); + return transformFileStatusToResourceMetadata(fileStatus); } @Override @@ -709,144 +261,21 @@ public class HdfsStorageOperator implements Closeable, StorageOperate { } } - /** - * yarn ha admin utils - */ - private static final class YarnHAAdminUtils { - - /** - * get active resourcemanager node - * - * @param protocol http protocol - * @param rmIds yarn ha ids - * @return yarn active node - */ - public static String getActiveRMName(String protocol, String rmIds) { - - String[] rmIdArr = rmIds.split(Constants.COMMA); - - String yarnUrl = - protocol + "%s:" + hdfsProperties.getHadoopResourceManagerHttpAddressPort() + "/ws/v1/cluster/info"; - - try { - - /** - * send http get request to rm - */ - - for (String rmId : rmIdArr) { - String state = getRMState(String.format(yarnUrl, rmId)); - if (Constants.HADOOP_RM_STATE_ACTIVE.equals(state)) { - return rmId; - } - } - - } catch (Exception e) { - log.error("yarn ha application url generation failed, message:{}", e.getMessage()); - } - return null; - } - - /** - * get ResourceManager state - */ - public static String getRMState(String url) { - - String retStr = Boolean.TRUE - .equals(hdfsProperties.isHadoopSecurityAuthStartupState()) - ? KerberosHttpClient.get(url) - : HttpUtils.get(url); - - if (StringUtils.isEmpty(retStr)) { - return null; - } - // to json - ObjectNode jsonObject = JSONUtils.parseObject(retStr); - - // get ResourceManager state - if (!jsonObject.has("clusterInfo")) { - return null; - } - return jsonObject.get("clusterInfo").path("haState").asText(); - } + private StorageEntity transformFileStatusToResourceMetadata(FileStatus fileStatus) { + Path fileStatusPath = fileStatus.getPath(); + String fileAbsolutePath = fileStatusPath.toString(); + ResourceMetadata resourceMetaData = getResourceMetaData(fileAbsolutePath); + return StorageEntity.builder() + .fileName(fileStatusPath.getName()) + .fullName(fileAbsolutePath) + .pfullName(resourceMetaData.getResourceParentAbsolutePath()) + .type(resourceMetaData.getResourceType()) + .isDirectory(fileStatus.isDirectory()) + .size(fileStatus.getLen()) + .createTime(new Date(fileStatus.getModificationTime())) + .updateTime(new Date(fileStatus.getModificationTime())) + .build(); } - @Override - public void deleteTenant(String tenantCode) throws Exception { - String tenantPath = getHdfsDataBasePath() + FOLDER_SEPARATOR + tenantCode; - - if (exists(tenantPath)) { - delete(tenantPath, true); - - } - } - - @Override - public ResUploadType returnStorageType() { - return ResUploadType.HDFS; - } - - @Override - public List listFilesStatusRecursively(String path, String defaultPath, String tenantCode, - ResourceType type) { - List storageEntityList = new ArrayList<>(); - - LinkedList foldersToFetch = new LinkedList<>(); - - do { - String pathToExplore = ""; - if (foldersToFetch.size() == 0) { - pathToExplore = path; - } else { - pathToExplore = foldersToFetch.pop().getFullName(); - } - - try { - List tempList = listFilesStatus(pathToExplore, defaultPath, tenantCode, type); - - for (StorageEntity temp : tempList) { - if (temp.isDirectory()) { - foldersToFetch.add(temp); - } - } - - storageEntityList.addAll(tempList); - } catch (FileNotFoundException e) { - log.error("Resource path: {}", pathToExplore, e); - // return the resources fetched before error occurs. - return storageEntityList; - } catch (IOException e) { - log.error("Resource path: {}", pathToExplore, e); - // return the resources fetched before error occurs. - return storageEntityList; - } - - } while (foldersToFetch.size() != 0); - - return storageEntityList; - - } - - /** - * find alias for directories, NOT for files - * a directory is a path ending with "/" - */ - private String findDirAlias(String myStr) { - if (!myStr.endsWith("/")) { - // Make sure system won't crush down if someone accidentally misuse the function. - return myStr; - } - int lastIndex = myStr.lastIndexOf("/"); - String subbedString = myStr.substring(0, lastIndex); - int secondLastIndex = subbedString.lastIndexOf("/"); - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(myStr, secondLastIndex + 1, lastIndex + 1); - - return stringBuilder.toString(); - } - - private String addFolderSeparatorIfNotExisted(String fullName) { - return fullName.endsWith(FOLDER_SEPARATOR) ? fullName : fullName + FOLDER_SEPARATOR; - } } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperatorFactory.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperatorFactory.java index d2a6ef0262..ed2aa60615 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperatorFactory.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperatorFactory.java @@ -17,20 +17,36 @@ package org.apache.dolphinscheduler.plugin.storage.hdfs; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperateFactory; +import static org.apache.dolphinscheduler.common.constants.Constants.FS_DEFAULT_FS; +import static org.apache.dolphinscheduler.common.constants.Constants.HDFS_ROOT_USER; + +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperatorFactory; import org.apache.dolphinscheduler.plugin.storage.api.StorageType; +import java.util.Map; + import com.google.auto.service.AutoService; -@AutoService(StorageOperateFactory.class) -public class HdfsStorageOperatorFactory implements StorageOperateFactory { +@AutoService(StorageOperatorFactory.class) +public class HdfsStorageOperatorFactory implements StorageOperatorFactory { @Override - public StorageOperate createStorageOperate() { - HdfsStorageOperator hdfsOperator = new HdfsStorageOperator(); - hdfsOperator.init(); - return hdfsOperator; + public StorageOperator createStorageOperate() { + final HdfsStorageProperties hdfsStorageProperties = getHdfsStorageProperties(); + return new HdfsStorageOperator(hdfsStorageProperties); + } + + private HdfsStorageProperties getHdfsStorageProperties() { + Map configurationProperties = PropertyUtils.getByPrefix("fs."); + return HdfsStorageProperties.builder() + .user(PropertyUtils.getString(HDFS_ROOT_USER)) + .defaultFS(PropertyUtils.getString(FS_DEFAULT_FS)) + .configurationProperties(configurationProperties) + .resourceUploadPath(PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler")) + .build(); } @Override diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageProperties.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageProperties.java index a40529f584..48a2f1d179 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageProperties.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageProperties.java @@ -17,66 +17,25 @@ package org.apache.dolphinscheduler.plugin.storage.hdfs; -import static org.apache.dolphinscheduler.common.constants.Constants.FS_DEFAULT_FS; -import static org.apache.dolphinscheduler.common.constants.Constants.HADOOP_RESOURCE_MANAGER_HTTPADDRESS_PORT; -import static org.apache.dolphinscheduler.common.constants.Constants.HADOOP_SECURITY_AUTHENTICATION_STARTUP_STATE; -import static org.apache.dolphinscheduler.common.constants.Constants.HDFS_ROOT_USER; -import static org.apache.dolphinscheduler.common.constants.Constants.KERBEROS_EXPIRE_TIME; -import static org.apache.dolphinscheduler.common.constants.Constants.YARN_APPLICATION_STATUS_ADDRESS; -import static org.apache.dolphinscheduler.common.constants.Constants.YARN_JOB_HISTORY_STATUS_ADDRESS; -import static org.apache.dolphinscheduler.common.constants.Constants.YARN_RESOURCEMANAGER_HA_RM_IDS; - -import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; - -import org.springframework.context.annotation.Configuration; +import lombok.NoArgsConstructor; @Data -@Configuration +@Builder +@NoArgsConstructor +@AllArgsConstructor public class HdfsStorageProperties { - /** - * HDFS storage user - */ - private String user = PropertyUtils.getString(HDFS_ROOT_USER); + private String user; - /** - * HDFS default fs - */ - private String defaultFS = PropertyUtils.getString(FS_DEFAULT_FS); + private String defaultFS; - /** - * YARN resource manager HA RM ids - */ - private String yarnResourceRmIds = PropertyUtils.getString(YARN_RESOURCEMANAGER_HA_RM_IDS); + private Map configurationProperties; - /** - * YARN application status address - */ - private String yarnAppStatusAddress = PropertyUtils.getString(YARN_APPLICATION_STATUS_ADDRESS); + private String resourceUploadPath; - /** - * YARN job history status address - */ - private String yarnJobHistoryStatusAddress = PropertyUtils.getString(YARN_JOB_HISTORY_STATUS_ADDRESS); - - /** - * Hadoop resouece manager http address port - */ - private String hadoopResourceManagerHttpAddressPort = - PropertyUtils.getString(HADOOP_RESOURCE_MANAGER_HTTPADDRESS_PORT); - - /** - * Hadoop security authentication startup state - */ - private boolean hadoopSecurityAuthStartupState = - PropertyUtils.getBoolean(HADOOP_SECURITY_AUTHENTICATION_STARTUP_STATE, false); - - /** - * Kerberos expire time - */ - public static int getKerberosExpireTime() { - return PropertyUtils.getInt(KERBEROS_EXPIRE_TIME, 2); - } } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperator.java index 173760b632..31c8da5074 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperator.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperator.java @@ -22,18 +22,8 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class LocalStorageOperator extends HdfsStorageOperator { - public LocalStorageOperator() { - super(new HdfsStorageProperties()); - } - public LocalStorageOperator(HdfsStorageProperties hdfsStorageProperties) { super(hdfsStorageProperties); } - @Override - public String getResourceFileName(String tenantCode, String fullName) { - // prefix schema `file:/` should be remove in local file mode - String fullNameRemoveSchema = fullName.replaceFirst(hdfsProperties.getDefaultFS(), ""); - return super.getResourceFileName(tenantCode, fullNameRemoveSchema); - } } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperatorFactory.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperatorFactory.java index 5f44eca87b..f0bb1543aa 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperatorFactory.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/main/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperatorFactory.java @@ -17,24 +17,32 @@ package org.apache.dolphinscheduler.plugin.storage.hdfs; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperateFactory; +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperatorFactory; import org.apache.dolphinscheduler.plugin.storage.api.StorageType; import com.google.auto.service.AutoService; -@AutoService(StorageOperateFactory.class) -public class LocalStorageOperatorFactory implements StorageOperateFactory { +@AutoService(StorageOperatorFactory.class) +public class LocalStorageOperatorFactory implements StorageOperatorFactory { - private static final String LOCAL_DEFAULT_FS = "file:/"; + public static final String LOCAL_DEFAULT_FS = "file:/"; @Override - public StorageOperate createStorageOperate() { - HdfsStorageProperties hdfsStorageProperties = new HdfsStorageProperties(); - hdfsStorageProperties.setDefaultFS(LOCAL_DEFAULT_FS); + public StorageOperator createStorageOperate() { + final HdfsStorageProperties hdfsStorageProperties = getHdfsStorageProperties(); return new LocalStorageOperator(hdfsStorageProperties); } + private HdfsStorageProperties getHdfsStorageProperties() { + return HdfsStorageProperties.builder() + .defaultFS(LOCAL_DEFAULT_FS) + .resourceUploadPath(PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler")) + .build(); + } + @Override public StorageType getStorageOperate() { return StorageType.LOCAL; diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperatorTest.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperatorTest.java index 63931cba78..ee7daa2418 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperatorTest.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/java/org/apache/dolphinscheduler/plugin/storage/hdfs/HdfsStorageOperatorTest.java @@ -17,62 +17,183 @@ package org.apache.dolphinscheduler.plugin.storage.hdfs; -import org.apache.dolphinscheduler.common.utils.HttpUtils; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; import org.apache.dolphinscheduler.spi.enums.ResourceType; -import org.junit.jupiter.api.Assertions; +import java.io.File; +import java.nio.file.FileAlreadyExistsException; +import java.time.Duration; +import java.util.List; +import java.util.stream.Stream; + +import lombok.SneakyThrows; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.testcontainers.containers.ComposeContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.lifecycle.Startables; +import org.testcontainers.shaded.com.google.common.collect.ImmutableMap; -/** - * hadoop utils test - */ -@ExtendWith(MockitoExtension.class) -public class HdfsStorageOperatorTest { +class HdfsStorageOperatorTest { - private static final Logger logger = LoggerFactory.getLogger(HdfsStorageOperatorTest.class); + private static HdfsStorageOperator storageOperator; - @Test - public void getHdfsTenantDir() { - HdfsStorageOperator hdfsStorageOperator = new HdfsStorageOperator(); - logger.info(hdfsStorageOperator.getHdfsTenantDir("1234")); - Assertions.assertTrue(true); + private static ComposeContainer hdfsContainer; + + @BeforeAll + public static void setUp() throws InterruptedException { + String hdfsDockerComposeFilePath = + HdfsStorageOperatorTest.class.getResource("/hadoop-docker-compose/docker-compose.yaml").getFile(); + hdfsContainer = new ComposeContainer(new File(hdfsDockerComposeFilePath)) + .withPull(true) + .withTailChildContainers(true) + .withLocalCompose(true) + .waitingFor("namenode", Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(60))) + .waitingFor("datanode", Wait.forHealthcheck().withStartupTimeout(Duration.ofSeconds(60))); + + Startables.deepStart(Stream.of(hdfsContainer)).join(); + + HdfsStorageProperties hdfsStorageProperties = HdfsStorageProperties.builder() + .resourceUploadPath("/tmp/dolphinscheduler") + .user("hadoop") + .defaultFS("hdfs://localhost") + // The default replication factor is 3, which is too large for the test environment. + // So we set it to 1. + .configurationProperties(ImmutableMap.of("dfs.replication", "1")) + .build(); + storageOperator = new HdfsStorageOperator(hdfsStorageProperties); + } + + @BeforeEach + public void initializeStorageFile() { + storageOperator.delete("hdfs://localhost/tmp/dolphinscheduler/test-default", true); + storageOperator.createStorageDir("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/empty"); + storageOperator.createStorageDir("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sql"); + // todo: upload file and add file case } @Test - public void getHdfsUdfFileName() { - HdfsStorageOperator hdfsStorageOperator = new HdfsStorageOperator(); - logger.info(hdfsStorageOperator.getHdfsUdfFileName("admin", "file_name")); - Assertions.assertTrue(true); + public void testGetResourceMetaData() { + ResourceMetadata resourceMetaData = + storageOperator.getResourceMetaData( + "hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sqlDirectory/demo.sql"); + assertEquals("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sqlDirectory/demo.sql", + resourceMetaData.getResourceAbsolutePath()); + assertEquals("hdfs://localhost/tmp/dolphinscheduler", resourceMetaData.getResourceBaseDirectory()); + assertEquals("test-default", resourceMetaData.getTenant()); + assertEquals(ResourceType.FILE, resourceMetaData.getResourceType()); + assertEquals("sqlDirectory/demo.sql", resourceMetaData.getResourceRelativePath()); + assertEquals("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sqlDirectory", + resourceMetaData.getResourceParentAbsolutePath()); + assertFalse(resourceMetaData.isDirectory()); } @Test - public void getHdfsResourceFileName() { - HdfsStorageOperator hdfsStorageOperator = new HdfsStorageOperator(); - logger.info(hdfsStorageOperator.getHdfsResourceFileName("admin", "file_name")); - Assertions.assertTrue(true); + public void testGetStorageBaseDirectory() { + assertEquals("hdfs://localhost/tmp/dolphinscheduler", storageOperator.getStorageBaseDirectory()); } @Test - public void getHdfsFileName() { - HdfsStorageOperator hdfsStorageOperator = new HdfsStorageOperator(); - logger.info(hdfsStorageOperator.getHdfsFileName(ResourceType.FILE, "admin", "file_name")); - Assertions.assertTrue(true); + public void testGetStorageBaseDirectory_withTenantCode() { + assertEquals("hdfs://localhost/tmp/dolphinscheduler/default", + storageOperator.getStorageBaseDirectory("default")); } @Test - public void getAppAddress() { - HdfsStorageOperator hdfsStorageOperator = new HdfsStorageOperator(); - try (MockedStatic mockedHttpUtils = Mockito.mockStatic(HttpUtils.class)) { - mockedHttpUtils.when(() -> HttpUtils.get("http://ds1:8088/ws/v1/cluster/info")) - .thenReturn("{\"clusterInfo\":{\"state\":\"STARTED\",\"haState\":\"ACTIVE\"}}"); - logger.info(hdfsStorageOperator.getAppAddress("http://ds1:8088/ws/v1/cluster/apps/%s", "ds1,ds2")); - Assertions.assertTrue(true); + public void testGetStorageBaseDirectory_withTenantCode_withFile() { + assertEquals("hdfs://localhost/tmp/dolphinscheduler/default/resources", + storageOperator.getStorageBaseDirectory("default", ResourceType.FILE)); + } + + @Test + public void testGetStorageBaseDirectory_withTenantCode_withAll() { + assertEquals("hdfs://localhost/tmp/dolphinscheduler/default", + storageOperator.getStorageBaseDirectory("default", ResourceType.ALL)); + } + + @Test + public void testGetStorageFileAbsolutePath() { + assertEquals("hdfs://localhost/tmp/dolphinscheduler/default/resources/a.sql", + storageOperator.getStorageFileAbsolutePath("default", "a.sql")); + } + + @Test + public void testCreateStorageDir_notExist() { + storageOperator.createStorageDir("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/newDirectory"); + storageOperator.exists("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/newDirectory"); + } + + @Test + public void testCreateStorageDir_exist() { + assertThrows(FileAlreadyExistsException.class, + () -> storageOperator + .createStorageDir("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/empty")); + } + + @Test + public void testExist_DirectoryExist() { + assertThat(storageOperator.exists("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sql")) + .isTrue(); + } + + @Test + public void testExist_DirectoryNotExist() { + assertThat( + storageOperator.exists("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/notExist")) + .isFalse(); + } + + @Test + public void testDelete_directoryExist() { + storageOperator.delete("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sql", true); + assertThat(storageOperator.exists("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sql")) + .isFalse(); + } + + @Test + public void testDelete_directoryNotExist() { + storageOperator.delete("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/non", true); + assertThat(storageOperator.exists("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/non")) + .isFalse(); + } + + @Test + public void testListStorageEntity_directory() { + List storageEntities = + storageOperator.listStorageEntity("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/"); + assertThat(storageEntities).hasSize(2); + } + + @Test + public void testGetStorageEntity_directory() { + StorageEntity storageEntity = + storageOperator.getStorageEntity("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sql"); + assertThat(storageEntity.getFullName()) + .isEqualTo("hdfs://localhost/tmp/dolphinscheduler/test-default/resources/sql"); + assertThat(storageEntity.isDirectory()).isTrue(); + assertThat(storageEntity.getPfullName()) + .isEqualTo("hdfs://localhost/tmp/dolphinscheduler/test-default/resources"); + assertThat(storageEntity.getType()).isEqualTo(ResourceType.FILE); + assertThat(storageEntity.getFileName()).isEqualTo("sql"); + } + + @SneakyThrows + @AfterAll + public static void tearDown() { + if (storageOperator != null) { + storageOperator.close(); + } + if (hdfsContainer != null) { + hdfsContainer.stop(); } } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperatorTest.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperatorTest.java new file mode 100644 index 0000000000..f6ed76e265 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/java/org/apache/dolphinscheduler/plugin/storage/hdfs/LocalStorageOperatorTest.java @@ -0,0 +1,321 @@ +/* + * 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.dolphinscheduler.plugin.storage.hdfs; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.FileUtils; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.spi.enums.ResourceType; + +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import lombok.SneakyThrows; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class LocalStorageOperatorTest { + + private StorageOperator storageOperator; + + private static final String resourceBaseDir = + Paths.get(LocalStorageOperatorTest.class.getResource("/").getFile(), "localStorage").toString(); + private static final String tenantCode = "default"; + private static final String baseDir = + Paths.get(resourceBaseDir, tenantCode, Constants.RESOURCE_TYPE_FILE).toString(); + + @SneakyThrows + @BeforeEach + public void setup() { + Files.createDirectories(Paths.get(resourceBaseDir)); + System.clearProperty(Constants.RESOURCE_UPLOAD_PATH); + System.setProperty(Constants.RESOURCE_UPLOAD_PATH, resourceBaseDir); + + LocalStorageOperatorFactory localStorageOperatorFactory = new LocalStorageOperatorFactory(); + storageOperator = localStorageOperatorFactory.createStorageOperate(); + // create file and directory + Files.createDirectories(Paths.get(baseDir, "sqlDirectory")); + Files.createDirectories(Paths.get(baseDir, "emptyDirectory")); + Files.createFile(Paths.get(baseDir, "sqlDirectory", "demo.sql")); + Files.write(Paths.get(baseDir, "sqlDirectory", "demo.sql"), "select * from demo".getBytes()); + + } + + @Test + public void testGetResourceMetaData_directory() { + String resourceFileAbsolutePath = "file:" + baseDir; + + ResourceMetadata resourceMetaData = storageOperator.getResourceMetaData(resourceFileAbsolutePath); + assertThat(resourceMetaData.getResourceAbsolutePath()).isEqualTo("file:" + baseDir); + assertThat(resourceMetaData.getResourceBaseDirectory()).isEqualTo("file:" + resourceBaseDir); + assertThat(resourceMetaData.getTenant()).isEqualTo("default"); + assertThat(resourceMetaData.getResourceType()).isEqualTo(ResourceType.FILE); + assertThat(resourceMetaData.getResourceRelativePath()).isEqualTo("/"); + } + + @Test + public void testGetResourceMetaData_file() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory", "demo.sql"); + + ResourceMetadata resourceMetaData = storageOperator.getResourceMetaData(resourceFileAbsolutePath); + assertThat(resourceMetaData.getResourceAbsolutePath()).isEqualTo(resourceFileAbsolutePath); + assertThat(resourceMetaData.getResourceBaseDirectory()).isEqualTo("file:" + resourceBaseDir); + assertThat(resourceMetaData.getTenant()).isEqualTo("default"); + assertThat(resourceMetaData.getResourceType()).isEqualTo(ResourceType.FILE); + assertThat(resourceMetaData.getResourceRelativePath()).isEqualTo("sqlDirectory/demo.sql"); + } + + @Test + public void testGetResourceMetaData_invalidatedPath() { + String resourceFileAbsolutePath = Paths.get(baseDir, "sqlDirectory", "demo.sql").toString(); + + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> storageOperator.getResourceMetaData(resourceFileAbsolutePath)); + assertThat(illegalArgumentException.getMessage()) + .isEqualTo("Invalid resource path: " + resourceFileAbsolutePath); + } + + @Test + public void testGetStorageBaseDirectory() { + String storageBaseDirectory = storageOperator.getStorageBaseDirectory(); + assertThat(storageBaseDirectory).isEqualTo("file:" + resourceBaseDir); + } + + @Test + public void testGetStorageBaseDirectory_withTenant() { + String storageBaseDirectory = storageOperator.getStorageBaseDirectory("default"); + assertThat(storageBaseDirectory).isEqualTo("file:" + Paths.get(resourceBaseDir, tenantCode)); + } + + @Test + public void testGetStorageBaseDirectory_withTenant_withResourceTypeFile() { + String storageBaseDirectory = storageOperator.getStorageBaseDirectory("default", ResourceType.FILE); + assertThat(storageBaseDirectory) + .isEqualTo("file:" + Paths.get(resourceBaseDir, tenantCode, Constants.RESOURCE_TYPE_FILE)); + } + + @Test + public void testGetStorageBaseDirectory_withTenant_withResourceTypeUdf() { + String storageBaseDirectory = storageOperator.getStorageBaseDirectory("default", ResourceType.UDF); + assertThat(storageBaseDirectory) + .isEqualTo("file:" + Paths.get(resourceBaseDir, tenantCode, Constants.RESOURCE_TYPE_UDF)); + } + + @Test + public void testGetStorageBaseDirectory_withTenant_withResourceTypeAll() { + String storageBaseDirectory = storageOperator.getStorageBaseDirectory("default", ResourceType.ALL); + assertThat(storageBaseDirectory).isEqualTo("file:" + Paths.get(resourceBaseDir, tenantCode)); + } + + @Test + public void testGetStorageBaseDirectory_withEmptyTenant_withResourceType() { + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> storageOperator.getStorageBaseDirectory("", ResourceType.ALL)); + assertThat(illegalArgumentException.getMessage()).isEqualTo("Tenant code should not be empty"); + } + + @Test + public void testGetStorageBaseDirectory_withTenant_withEmptyResourceType() { + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> storageOperator.getStorageBaseDirectory("default", null)); + assertThat(illegalArgumentException.getMessage()).isEqualTo("Resource type should not be null"); + } + + @Test + public void testGetStorageFileAbsolutePath() { + String fileAbsolutePath = storageOperator.getStorageFileAbsolutePath("default", "test.sh"); + assertThat(fileAbsolutePath).isEqualTo( + "file:" + Paths.get(resourceBaseDir, tenantCode, Constants.RESOURCE_TYPE_FILE, "test.sh")); + } + + @SneakyThrows + @Test + public void testCreateStorageDir_notExists() { + String testDirFileAbsolutePath = + "file:" + Paths.get(resourceBaseDir, "root", Constants.RESOURCE_TYPE_FILE, "testDir"); + try { + storageOperator.createStorageDir(testDirFileAbsolutePath); + StorageEntity storageEntity = storageOperator.getStorageEntity(testDirFileAbsolutePath); + assertThat(storageEntity.getFullName()).isEqualTo(testDirFileAbsolutePath); + assertThat(storageEntity.getFileName()).isEqualTo("testDir"); + assertThat(storageEntity.getPfullName()) + .isEqualTo("file:" + Paths.get(resourceBaseDir, "root", Constants.RESOURCE_TYPE_FILE)); + assertThat(storageEntity.isDirectory()).isTrue(); + assertThat(storageEntity.getType()).isEqualTo(ResourceType.FILE); + } finally { + storageOperator.delete(testDirFileAbsolutePath, true); + } + } + + @SneakyThrows + @Test + public void testCreateStorageDir_exists() { + String testDirFileAbsolutePath = + "file:" + Paths.get(resourceBaseDir, "default", Constants.RESOURCE_TYPE_FILE, "sqlDirectory"); + assertThrows(FileAlreadyExistsException.class, () -> storageOperator.createStorageDir(testDirFileAbsolutePath)); + } + + @Test + public void testExists_fileExist() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory", "demo.sql"); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isTrue(); + } + + @Test + public void testExists_fileNotExist() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory", "demo.sh"); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isFalse(); + } + + @Test + public void testExists_directoryExist() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory"); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isTrue(); + } + + @Test + public void testExists_directoryNotExist() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "shellDirectory"); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isFalse(); + } + + @Test + public void testDelete_directoryExist() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory"); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isTrue(); + + storageOperator.delete(resourceFileAbsolutePath, true); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isFalse(); + } + + @Test + public void testDelete_directoryNotExist() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "shellDirectory"); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isFalse(); + + storageOperator.delete(resourceFileAbsolutePath, true); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isFalse(); + } + + @Test + public void testDelete_fileExist() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory", "demo.sql"); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isTrue(); + + storageOperator.delete(resourceFileAbsolutePath, true); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isFalse(); + } + + @Test + public void testDelete_fileNotExist() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory", "demo.sh"); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isFalse(); + + storageOperator.delete(resourceFileAbsolutePath, true); + assertThat(storageOperator.exists(resourceFileAbsolutePath)).isFalse(); + } + + @Test + public void testFetchFileContent() { + // todo: add large file test case + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory", "demo.sql"); + List content = storageOperator.fetchFileContent(resourceFileAbsolutePath, 0, 10); + assertThat(content).containsExactly("select * from demo"); + + } + + @Test + public void testListStorageEntity_directoryNotEmpty() { + String resourceFileAbsolutePath = "file:" + baseDir; + List storageEntities = storageOperator.listStorageEntity(resourceFileAbsolutePath); + assertThat(storageEntities.size()).isEqualTo(2); + + StorageEntity storageEntity1 = storageEntities.get(0); + assertThat(storageEntity1.getFullName()).isEqualTo("file:" + baseDir + "/emptyDirectory"); + assertThat(storageEntity1.getFileName()).isEqualTo("emptyDirectory"); + assertThat(storageEntity1.getPfullName()).isEqualTo("file:" + baseDir); + assertThat(storageEntity1.isDirectory()).isTrue(); + assertThat(storageEntity1.getType()).isEqualTo(ResourceType.FILE); + + StorageEntity storageEntity2 = storageEntities.get(1); + assertThat(storageEntity2.getFullName()).isEqualTo("file:" + baseDir + "/sqlDirectory"); + assertThat(storageEntity2.getFileName()).isEqualTo("sqlDirectory"); + assertThat(storageEntity2.getPfullName()).isEqualTo("file:" + baseDir); + assertThat(storageEntity2.isDirectory()).isTrue(); + assertThat(storageEntity2.getType()).isEqualTo(ResourceType.FILE); + } + + @Test + public void testListStorageEntity_directoryEmpty() { + String resourceFileAbsolutePath = "file:" + baseDir + "/emptyDirectory"; + List storageEntities = storageOperator.listStorageEntity(resourceFileAbsolutePath); + assertThat(storageEntities.size()).isEqualTo(0); + } + + @Test + public void testListStorageEntity_directoryNotExist() { + String resourceFileAbsolutePath = "file:" + baseDir + "/notExistDirectory"; + assertThat(storageOperator.listStorageEntity(resourceFileAbsolutePath)).isEmpty(); + } + + @Test + public void testListStorageEntity_file() { + String resourceFileAbsolutePath = "file:" + Paths.get(baseDir, "sqlDirectory", "demo.sql"); + List storageEntities = storageOperator.listStorageEntity(resourceFileAbsolutePath); + assertThat(storageEntities.size()).isEqualTo(1); + + StorageEntity storageEntity = storageEntities.get(0); + assertThat(storageEntity.getFullName()).isEqualTo("file:" + Paths.get(baseDir, "sqlDirectory", "demo.sql")); + assertThat(storageEntity.getFileName()).isEqualTo("demo.sql"); + assertThat(storageEntity.getPfullName()).isEqualTo("file:" + Paths.get(baseDir, "sqlDirectory")); + assertThat(storageEntity.isDirectory()).isFalse(); + assertThat(storageEntity.getType()).isEqualTo(ResourceType.FILE); + + } + + @Test + public void testListStorageEntityRecursively_directory() { + String resourceFileAbsolutePath = "file:" + baseDir; + List storageEntities = + storageOperator.listFileStorageEntityRecursively(resourceFileAbsolutePath); + assertThat(storageEntities.size()).isEqualTo(1); + + StorageEntity storageEntity2 = storageEntities.get(0); + assertThat(storageEntity2.getFullName()).isEqualTo("file:" + Paths.get(baseDir, "sqlDirectory", "demo.sql")); + assertThat(storageEntity2.getFileName()).isEqualTo("demo.sql"); + assertThat(storageEntity2.getPfullName()).isEqualTo("file:" + Paths.get(baseDir, "sqlDirectory")); + assertThat(storageEntity2.isDirectory()).isFalse(); + assertThat(storageEntity2.getType()).isEqualTo(ResourceType.FILE); + } + + @SneakyThrows + @AfterEach + public void after() { + FileUtils.deleteFile(resourceBaseDir); + } + +} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/hadoop-docker-compose/docker-compose.yaml b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/hadoop-docker-compose/docker-compose.yaml new file mode 100644 index 0000000000..60ede8faf8 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/hadoop-docker-compose/docker-compose.yaml @@ -0,0 +1,110 @@ +# 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. +version: "3" + +services: + namenode: + image: sbloodys/hadoop:3.3.6 + hostname: namenode + command: [ "hdfs", "namenode" ] + ports: + - 9870:9870 + - 8020:8020 + env_file: + - ./hadoop.env + 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: sbloodys/hadoop:3.3.6 + hostname: datanode + command: [ "hdfs", "datanode" ] + env_file: + - ./hadoop.env + logging: + driver: "json-file" + options: + max-size: "200m" + max-file: "1" + ports: + - 9864:9864 + 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: sbloodys/hadoop:3.3.6 + hostname: resourcemanager + command: [ "yarn", "resourcemanager" ] + ports: + - 8088:8088 + env_file: + - ./hadoop.env + 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: sbloodys/hadoop:3.3.6 + hostname: nodemanager + command: [ "yarn", "nodemanager" ] + env_file: + - ./hadoop.env + 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 diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/hadoop-docker-compose/hadoop.env b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/hadoop-docker-compose/hadoop.env new file mode 100644 index 0000000000..701cf3471f --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/hadoop-docker-compose/hadoop.env @@ -0,0 +1,45 @@ +# +# 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 +CORE-SITE.XML_hadoop.http.staticuser.user=hadoop +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 +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.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 \ No newline at end of file diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/storage/hello.sh b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/storage/hello.sh new file mode 100644 index 0000000000..9ee3583189 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/storage/hello.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# 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. +# +echo hello \ No newline at end of file diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/storage/sql/demo.sql b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/storage/sql/demo.sql new file mode 100644 index 0000000000..83ebbac8d0 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-hdfs/src/test/resources/storage/sql/demo.sql @@ -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. +# + +select * from t; \ No newline at end of file diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperator.java index b644210048..6f67ccb363 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperator.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperator.java @@ -17,19 +17,12 @@ package org.apache.dolphinscheduler.plugin.storage.obs; -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_FILE; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_UDF; - import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; import org.apache.dolphinscheduler.common.utils.FileUtils; -import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.AbstractStorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.plugin.task.api.TaskConstants; -import org.apache.dolphinscheduler.spi.enums.ResourceType; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.lang3.StringUtils; @@ -37,153 +30,83 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.Data; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import com.obs.services.ObsClient; import com.obs.services.exception.ObsException; -import com.obs.services.internal.ServiceException; -import com.obs.services.model.DeleteObjectsRequest; -import com.obs.services.model.GetObjectRequest; import com.obs.services.model.ListObjectsRequest; import com.obs.services.model.ObjectListing; import com.obs.services.model.ObjectMetadata; import com.obs.services.model.ObsObject; import com.obs.services.model.PutObjectRequest; -@Data @Slf4j -public class ObsStorageOperator implements Closeable, StorageOperate { +public class ObsStorageOperator extends AbstractStorageOperator implements Closeable, StorageOperator { - private String accessKeyId; + private final String bucketName; - private String accessKeySecret; + private final ObsClient obsClient; - private String bucketName; - - private String endPoint; - - private ObsClient obsClient; - - public ObsStorageOperator() { - } - - public void init() { - this.accessKeyId = readObsAccessKeyID(); - this.accessKeySecret = readObsAccessKeySecret(); - this.endPoint = readObsEndPoint(); - this.bucketName = readObsBucketName(); - this.obsClient = buildObsClient(); + public ObsStorageOperator(ObsStorageProperties obsStorageProperties) { + super(obsStorageProperties.getResourceUploadPath()); + this.bucketName = obsStorageProperties.getBucketName(); + this.obsClient = new ObsClient( + obsStorageProperties.getAccessKeyId(), + obsStorageProperties.getAccessKeySecret(), + obsStorageProperties.getEndPoint()); ensureBucketSuccessfullyCreated(bucketName); } - protected String readObsAccessKeyID() { - return PropertyUtils.getString(TaskConstants.HUAWEI_CLOUD_ACCESS_KEY_ID); - } - - protected String readObsAccessKeySecret() { - return PropertyUtils.getString(TaskConstants.HUAWEI_CLOUD_ACCESS_KEY_SECRET); - } - - protected String readObsBucketName() { - return PropertyUtils.getString(Constants.HUAWEI_CLOUD_OBS_BUCKET_NAME); - } - - protected String readObsEndPoint() { - return PropertyUtils.getString(Constants.HUAWEI_CLOUD_OBS_END_POINT); - } - @Override public void close() throws IOException { obsClient.close(); } @Override - public void createTenantDirIfNotExists(String tenantCode) throws Exception { - mkdir(tenantCode, getObsResDir(tenantCode)); - mkdir(tenantCode, getObsUdfDir(tenantCode)); - } - - @Override - public String getResDir(String tenantCode) { - return getObsResDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public String getUdfDir(String tenantCode) { - return getObsUdfDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public boolean mkdir(String tenantCode, String path) throws IOException { - final String key = path + FOLDER_SEPARATOR; - if (!obsClient.doesObjectExist(bucketName, key)) { - createObsPrefix(bucketName, key); + public String getStorageBaseDirectory() { + // All directory should end with File.separator + if (resourceBaseAbsolutePath.startsWith("/")) { + log.warn("{} -> {} should not start with / in obs", Constants.RESOURCE_UPLOAD_PATH, + resourceBaseAbsolutePath); + return resourceBaseAbsolutePath.substring(1); } - return true; + return resourceBaseAbsolutePath; } - protected void createObsPrefix(final String bucketName, final String key) { + @SneakyThrows + @Override + public void createStorageDir(String directoryAbsolutePath) { + directoryAbsolutePath = transformAbsolutePathToObsKey(directoryAbsolutePath); + if (obsClient.doesObjectExist(bucketName, directoryAbsolutePath)) { + throw new FileAlreadyExistsException("directory: " + directoryAbsolutePath + " already exists"); + } ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentLength(0L); InputStream emptyContent = new ByteArrayInputStream(new byte[0]); - PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, emptyContent); + PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, directoryAbsolutePath, emptyContent); obsClient.putObject(putObjectRequest); } + @SneakyThrows @Override - public String getResourceFullName(String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return String.format(FORMAT_S_S, getObsResDir(tenantCode), fileName); - } + public void download(String srcFilePath, String dstFilePath, boolean overwrite) { + srcFilePath = transformAbsolutePathToObsKey(srcFilePath); - @Override - public String getFileName(ResourceType resourceType, String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return getDir(resourceType, tenantCode) + fileName; - } - - @Override - public boolean delete(String fullName, List childrenPathList, boolean recursive) throws IOException { - // append the resource fullName to the list for deletion. - childrenPathList.add(fullName); - - DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName); - for (String deleteKys : childrenPathList) { - deleteObjectsRequest.addKeyAndVersion(deleteKys); - } - - try { - obsClient.deleteObjects(deleteObjectsRequest); - } catch (Exception e) { - log.error("delete objects error", e); - return false; - } - - return true; - } - - @Override - public void download(String srcFilePath, String dstFilePath, boolean overwrite) throws IOException { File dstFile = new File(dstFilePath); if (dstFile.isDirectory()) { Files.delete(dstFile.toPath()); @@ -199,277 +122,115 @@ public class ObsStorageOperator implements Closeable, StorageOperate { while ((readLen = obsInputStream.read(readBuf)) > 0) { fos.write(readBuf, 0, readLen); } - } catch (ObsException e) { - throw new IOException(e); - } catch (FileNotFoundException e) { - log.error("cannot find the destination file {}", dstFilePath); - throw e; } } @Override - public boolean exists(String fileName) throws IOException { + public boolean exists(String fileName) { + fileName = transformAbsolutePathToObsKey(fileName); return obsClient.doesObjectExist(bucketName, fileName); } @Override - public boolean delete(String filePath, boolean recursive) throws IOException { - try { - obsClient.deleteObject(bucketName, filePath); - return true; - } catch (ObsException e) { - log.error("fail to delete the object, the resource path is {}", filePath, e); - return false; - } + public void delete(String filePath, boolean recursive) { + filePath = transformAbsolutePathToObsKey(filePath); + obsClient.deleteObject(bucketName, filePath); } @Override - public boolean copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) throws IOException { + public void copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) { + srcPath = transformAbsolutePathToObsKey(srcPath); + dstPath = transformAbsolutePathToObsKey(dstPath); obsClient.copyObject(bucketName, srcPath, bucketName, dstPath); if (deleteSource) { obsClient.deleteObject(bucketName, srcPath); } - return true; } + @SneakyThrows @Override - public String getDir(ResourceType resourceType, String tenantCode) { - switch (resourceType) { - case UDF: - return getUdfDir(tenantCode); - case FILE: - return getResDir(tenantCode); - case ALL: - return getObsDataBasePath(); - default: - return ""; - } - } - - @Override - public boolean upload(String tenantCode, String srcFile, String dstPath, boolean deleteSource, - boolean overwrite) throws IOException { - try { - obsClient.putObject(bucketName, dstPath, new File(srcFile)); - if (deleteSource) { - Files.delete(Paths.get(srcFile)); + public void upload(String srcFile, String dstPath, boolean deleteSource, boolean overwrite) { + dstPath = transformAbsolutePathToObsKey(dstPath); + if (obsClient.doesObjectExist(bucketName, dstPath)) { + if (!overwrite) { + throw new ObsException("file: " + dstPath + " already exists"); + } else { + obsClient.deleteObject(bucketName, dstPath); } - return true; - } catch (ObsException e) { - log.error("upload failed, the bucketName is {}, the filePath is {}", bucketName, dstPath, e); - return false; } + obsClient.putObject(bucketName, dstPath, new File(srcFile)); + if (deleteSource) { + Files.delete(Paths.get(srcFile)); + } + } + @SneakyThrows @Override - public List vimFile(String tenantCode, String filePath, int skipLineNums, int limit) throws IOException { - if (StringUtils.isBlank(filePath)) { - log.error("file path:{} is empty", filePath); - return Collections.emptyList(); - } + public List fetchFileContent(String filePath, int skipLineNums, int limit) { + filePath = transformAbsolutePathToObsKey(filePath); ObsObject obsObject = obsClient.getObject(bucketName, filePath); - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(obsObject.getObjectContent()))) { - Stream stream = bufferedReader.lines().skip(skipLineNums).limit(limit); - return stream.collect(Collectors.toList()); + try ( + InputStreamReader inputStreamReader = new InputStreamReader(obsObject.getObjectContent()); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + return bufferedReader + .lines() + .skip(skipLineNums) + .limit(limit) + .collect(Collectors.toList()); } } @Override - public ResUploadType returnStorageType() { - return ResUploadType.OBS; - } - - @Override - public List listFilesStatusRecursively(String path, String defaultPath, String tenantCode, - ResourceType type) { - List storageEntityList = new ArrayList<>(); - LinkedList foldersToFetch = new LinkedList<>(); - - StorageEntity initialEntity = null; - try { - initialEntity = getFileStatus(path, defaultPath, tenantCode, type); - } catch (Exception e) { - log.error("error while listing files status recursively, path: {}", path, e); - return storageEntityList; - } - foldersToFetch.add(initialEntity); - - while (!foldersToFetch.isEmpty()) { - String pathToExplore = foldersToFetch.pop().getFullName(); - try { - List tempList = listFilesStatus(pathToExplore, defaultPath, tenantCode, type); - for (StorageEntity temp : tempList) { - if (temp.isDirectory()) { - foldersToFetch.add(temp); - } - } - storageEntityList.addAll(tempList); - } catch (Exception e) { - log.error("error while listing files stat:wus recursively, path: {}", pathToExplore, e); - } - } - - return storageEntityList; - } - - @Override - public List listFilesStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception { - List storageEntityList = new ArrayList<>(); + public List listStorageEntity(String resourceAbsolutePath) { + resourceAbsolutePath = transformObsKeyToAbsolutePath(resourceAbsolutePath); ListObjectsRequest request = new ListObjectsRequest(); request.setBucketName(bucketName); - request.setPrefix(path); - request.setDelimiter(FOLDER_SEPARATOR); - ObjectListing result = null; - try { - result = obsClient.listObjects(request); - } catch (Exception e) { - throw new ServiceException("Get ObsClient file list exception", e); - } + request.setPrefix(resourceAbsolutePath); + request.setDelimiter("/"); - while (result != null) { - String nextMarker = result.getNextMarker(); - List objects = result.getObjects(); + ObjectListing result = obsClient.listObjects(request); - for (ObsObject object : objects) { - if (!object.getObjectKey().endsWith(FOLDER_SEPARATOR)) { - // the path is a file - String[] aliasArr = object.getObjectKey().split(FOLDER_SEPARATOR); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, object.getObjectKey()); + return result.getObjects() + .stream() + .map(this::transformObsObjectToStorageEntity) + .collect(Collectors.toList()); + } - StorageEntity entity = new StorageEntity(); - ObjectMetadata metadata = object.getMetadata(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(object.getObjectKey()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(metadata.getContentLength()); - entity.setCreateTime(metadata.getLastModified()); - entity.setUpdateTime(metadata.getLastModified()); - entity.setPfullName(path); + @Override + public List listFileStorageEntityRecursively(String resourceAbsolutePath) { + resourceAbsolutePath = transformObsKeyToAbsolutePath(resourceAbsolutePath); - storageEntityList.add(entity); + Set visited = new HashSet<>(); + List storageEntityList = new ArrayList<>(); + LinkedList foldersToFetch = new LinkedList<>(); + foldersToFetch.addLast(resourceAbsolutePath); + + while (!foldersToFetch.isEmpty()) { + String pathToExplore = foldersToFetch.pop(); + visited.add(pathToExplore); + List tempList = listStorageEntity(pathToExplore); + for (StorageEntity temp : tempList) { + if (temp.isDirectory()) { + if (visited.contains(temp.getFullName())) { + continue; + } + foldersToFetch.add(temp.getFullName()); } } - - for (String commonPrefix : result.getCommonPrefixes()) { - // the paths in commonPrefix are directories - String suffix = StringUtils.difference(path, commonPrefix); - String fileName = StringUtils.difference(defaultPath, commonPrefix); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(suffix); - entity.setFileName(fileName); - entity.setFullName(commonPrefix); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - entity.setCreateTime(null); - entity.setUpdateTime(null); - entity.setPfullName(path); - - storageEntityList.add(entity); - } - - if (!StringUtils.isNotBlank(nextMarker)) { - break; - } - - request.setMarker(nextMarker); - try { - result = obsClient.listObjects(request); - } catch (Exception e) { - throw new ServiceException("Get ObsClient file list exception", e); - } + storageEntityList.addAll(tempList); } + return storageEntityList; } @Override - public StorageEntity getFileStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception { + public StorageEntity getStorageEntity(String resourceAbsolutePath) { + resourceAbsolutePath = transformObsKeyToAbsolutePath(resourceAbsolutePath); - if (path.endsWith(FOLDER_SEPARATOR)) { - // the path is a directory that may or may not exist in ObsClient - String alias = findDirAlias(path); - String fileName = StringUtils.difference(defaultPath, path); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(path); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - - return entity; - - } else { - GetObjectRequest request = new GetObjectRequest(); - request.setBucketName(bucketName); - request.setObjectKey(path); - ObsObject object; - try { - object = obsClient.getObject(request); - } catch (Exception e) { - throw new ServiceException("Get ObsClient file list exception", e); - } - - String[] aliasArr = object.getObjectKey().split(FOLDER_SEPARATOR); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, object.getObjectKey()); - - StorageEntity entity = new StorageEntity(); - ObjectMetadata metadata = object.getMetadata(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(object.getObjectKey()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(metadata.getContentLength()); - entity.setCreateTime(metadata.getLastModified()); - entity.setUpdateTime(metadata.getLastModified()); - - return entity; - } - - } - - @Override - public void deleteTenant(String tenantCode) throws Exception { - deleteTenantCode(tenantCode); - } - - public String getObsResDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_FILE, getObsTenantDir(tenantCode)); - } - - public String getObsUdfDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_UDF, getObsTenantDir(tenantCode)); - } - - public String getObsTenantDir(String tenantCode) { - return String.format(FORMAT_S_S, getObsDataBasePath(), tenantCode); - } - - public String getObsDataBasePath() { - if (FOLDER_SEPARATOR.equals(RESOURCE_UPLOAD_PATH)) { - return ""; - } else { - return RESOURCE_UPLOAD_PATH.replaceFirst(FOLDER_SEPARATOR, ""); - } - } - - protected void deleteTenantCode(String tenantCode) { - deleteDir(getResDir(tenantCode)); - deleteDir(getUdfDir(tenantCode)); + ObsObject object = obsClient.getObject(bucketName, resourceAbsolutePath); + return transformObsObjectToStorageEntity(object); } public void ensureBucketSuccessfullyCreated(String bucketName) { @@ -486,22 +247,37 @@ public class ObsStorageOperator implements Closeable, StorageOperate { log.info("bucketName: {} has been found", bucketName); } - protected void deleteDir(String directoryName) { - if (obsClient.doesObjectExist(bucketName, directoryName)) { - obsClient.deleteObject(bucketName, directoryName); + protected StorageEntity transformObsObjectToStorageEntity(ObsObject object) { + ObjectMetadata metadata = object.getMetadata(); + String fileAbsolutePath = transformObsKeyToAbsolutePath(object.getObjectKey()); + ResourceMetadata resourceMetaData = getResourceMetaData(fileAbsolutePath); + String fileExtension = com.google.common.io.Files.getFileExtension(resourceMetaData.getResourceAbsolutePath()); + + return StorageEntity.builder() + .fileName(new File(fileAbsolutePath).getName()) + .fullName(fileAbsolutePath) + .pfullName(resourceMetaData.getResourceParentAbsolutePath()) + .type(resourceMetaData.getResourceType()) + .isDirectory(StringUtils.isEmpty(fileExtension)) + .size(metadata.getContentLength()) + .createTime(metadata.getLastModified()) + .updateTime(metadata.getLastModified()) + .build(); + } + + private String transformAbsolutePathToObsKey(String absolutePath) { + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + if (resourceMetaData.isDirectory()) { + return FileUtils.concatFilePath(absolutePath, "/"); } + return absolutePath; } - protected ObsClient buildObsClient() { - return new ObsClient(accessKeyId, accessKeySecret, endPoint); - } - - private String findDirAlias(String dirPath) { - if (!dirPath.endsWith(FOLDER_SEPARATOR)) { - return dirPath; + private String transformObsKeyToAbsolutePath(String s3Key) { + if (s3Key.endsWith("/")) { + return s3Key.substring(0, s3Key.length() - 1); } - - Path path = Paths.get(dirPath); - return path.getName(path.getNameCount() - 1) + FOLDER_SEPARATOR; + return s3Key; } + } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperatorFactory.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperatorFactory.java index 2e67103931..66ba23ee48 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperatorFactory.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperatorFactory.java @@ -17,23 +17,32 @@ package org.apache.dolphinscheduler.plugin.storage.obs; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperateFactory; +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperatorFactory; import org.apache.dolphinscheduler.plugin.storage.api.StorageType; +import org.apache.dolphinscheduler.plugin.task.api.TaskConstants; import com.google.auto.service.AutoService; -@AutoService({StorageOperateFactory.class}) -public class ObsStorageOperatorFactory implements StorageOperateFactory { - - public ObsStorageOperatorFactory() { - } +@AutoService({StorageOperatorFactory.class}) +public class ObsStorageOperatorFactory implements StorageOperatorFactory { @Override - public StorageOperate createStorageOperate() { - ObsStorageOperator ossOperator = new ObsStorageOperator(); - ossOperator.init(); - return ossOperator; + public StorageOperator createStorageOperate() { + final ObsStorageProperties obsStorageProperties = getObsStorageProperties(); + return new ObsStorageOperator(obsStorageProperties); + } + + private ObsStorageProperties getObsStorageProperties() { + return ObsStorageProperties.builder() + .accessKeyId(PropertyUtils.getString(TaskConstants.HUAWEI_CLOUD_ACCESS_KEY_ID)) + .accessKeySecret(PropertyUtils.getString(TaskConstants.HUAWEI_CLOUD_ACCESS_KEY_SECRET)) + .bucketName(PropertyUtils.getString(Constants.HUAWEI_CLOUD_OBS_BUCKET_NAME)) + .endPoint(PropertyUtils.getString(Constants.HUAWEI_CLOUD_OBS_END_POINT)) + .resourceUploadPath(PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler")) + .build(); } @Override diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageProperties.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageProperties.java new file mode 100644 index 0000000000..a71e9b21b7 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/main/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageProperties.java @@ -0,0 +1,40 @@ +/* + * 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.dolphinscheduler.plugin.storage.obs; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ObsStorageProperties { + + private String accessKeyId; + + private String accessKeySecret; + + private String bucketName; + + private String endPoint; + + private String resourceUploadPath; +} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/test/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperatorTest.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/test/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperatorTest.java deleted file mode 100644 index 4aabdb6bd8..0000000000 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-obs/src/test/java/org/apache/dolphinscheduler/plugin/storage/obs/ObsStorageOperatorTest.java +++ /dev/null @@ -1,291 +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.dolphinscheduler.plugin.storage.obs; - -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.spi.enums.ResourceType; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.obs.services.ObsClient; - -@ExtendWith(MockitoExtension.class) -public class ObsStorageOperatorTest { - - private static final String ACCESS_KEY_ID_MOCK = "ACCESS_KEY_ID_MOCK"; - private static final String ACCESS_KEY_SECRET_MOCK = "ACCESS_KEY_SECRET_MOCK"; - private static final String END_POINT_MOCK = "END_POINT_MOCK"; - private static final String BUCKET_NAME_MOCK = "BUCKET_NAME_MOCK"; - private static final String TENANT_CODE_MOCK = "TENANT_CODE_MOCK"; - private static final String DIR_MOCK = "DIR_MOCK"; - private static final String FILE_NAME_MOCK = "FILE_NAME_MOCK"; - private static final String FILE_PATH_MOCK = "FILE_PATH_MOCK"; - private static final String FULL_NAME = "/tmp/dir1/"; - - private static final String DEFAULT_PATH = "/tmp/"; - @Mock - private ObsClient obsClientMock; - - private ObsStorageOperator obsOperator; - - @BeforeEach - public void setUp() throws Exception { - obsOperator = spy(new ObsStorageOperator()); - doReturn(ACCESS_KEY_ID_MOCK).when(obsOperator) - .readObsAccessKeyID(); - doReturn(ACCESS_KEY_SECRET_MOCK).when(obsOperator) - .readObsAccessKeySecret(); - doReturn(BUCKET_NAME_MOCK).when(obsOperator).readObsBucketName(); - doReturn(END_POINT_MOCK).when(obsOperator).readObsEndPoint(); - doReturn(obsClientMock).when(obsOperator).buildObsClient(); - doNothing().when(obsOperator).ensureBucketSuccessfullyCreated(any()); - - obsOperator.init(); - - } - - @Test - public void initObsOperator() { - verify(obsOperator, times(1)).buildObsClient(); - Assertions.assertEquals(ACCESS_KEY_ID_MOCK, obsOperator.getAccessKeyId()); - Assertions.assertEquals(ACCESS_KEY_SECRET_MOCK, obsOperator.getAccessKeySecret()); - Assertions.assertEquals(BUCKET_NAME_MOCK, obsOperator.getBucketName()); - } - - @Test - public void tearDownObsOperator() throws IOException { - doNothing().when(obsClientMock).close(); - obsOperator.close(); - verify(obsClientMock, times(1)).close(); - } - - @Test - public void createTenantResAndUdfDir() throws Exception { - doReturn(DIR_MOCK).when(obsOperator).getObsResDir(TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(obsOperator).getObsUdfDir(TENANT_CODE_MOCK); - doReturn(true).when(obsOperator).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - obsOperator.createTenantDirIfNotExists(TENANT_CODE_MOCK); - verify(obsOperator, times(2)).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - } - - @Test - public void getResDir() { - final String expectedResourceDir = String.format("dolphinscheduler/%s/resources/", TENANT_CODE_MOCK); - final String dir = obsOperator.getResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedResourceDir, dir); - } - - @Test - public void getUdfDir() { - final String expectedUdfDir = String.format("dolphinscheduler/%s/udfs/", TENANT_CODE_MOCK); - final String dir = obsOperator.getUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedUdfDir, dir); - } - - @Test - public void mkdirWhenDirExists() { - boolean isSuccess = false; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - doReturn(true).when(obsClientMock).doesObjectExist(BUCKET_NAME_MOCK, key); - isSuccess = obsOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - verify(obsClientMock, times(1)).doesObjectExist(BUCKET_NAME_MOCK, key); - - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); - } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void mkdirWhenDirNotExists() { - boolean isSuccess = true; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - doReturn(false).when(obsClientMock).doesObjectExist(BUCKET_NAME_MOCK, key); - doNothing().when(obsOperator).createObsPrefix(BUCKET_NAME_MOCK, key); - isSuccess = obsOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - verify(obsClientMock, times(1)).doesObjectExist(BUCKET_NAME_MOCK, key); - verify(obsOperator, times(1)).createObsPrefix(BUCKET_NAME_MOCK, key); - - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); - } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void getResourceFullName() { - final String expectedResourceFullName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFullName = obsOperator.getResourceFullName(TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedResourceFullName, resourceFullName); - } - - @Test - public void getResourceFileName() { - final String expectedResourceFileName = FILE_NAME_MOCK; - final String resourceFullName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFileName = obsOperator.getResourceFileName(TENANT_CODE_MOCK, resourceFullName); - Assertions.assertEquals(expectedResourceFileName, resourceFileName); - } - - @Test - public void getFileName() { - final String expectedFileName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String fileName = obsOperator.getFileName(ResourceType.FILE, TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedFileName, fileName); - } - - @Test - public void exists() { - boolean doesExist = false; - doReturn(true).when(obsClientMock).doesObjectExist(BUCKET_NAME_MOCK, FILE_NAME_MOCK); - try { - doesExist = obsOperator.exists(FILE_NAME_MOCK); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(doesExist); - verify(obsClientMock, times(1)).doesObjectExist(BUCKET_NAME_MOCK, FILE_NAME_MOCK); - } - - @Test - public void delete() { - boolean isDeleted = false; - doReturn(null).when(obsClientMock).deleteObject(anyString(), anyString()); - try { - isDeleted = obsOperator.delete(FILE_NAME_MOCK, true); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isDeleted); - verify(obsClientMock, times(1)).deleteObject(anyString(), anyString()); - } - - @Test - public void copy() { - boolean isSuccess = false; - doReturn(null).when(obsClientMock).copyObject(anyString(), anyString(), anyString(), anyString()); - try { - isSuccess = obsOperator.copy(FILE_PATH_MOCK, FILE_PATH_MOCK, false, false); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isSuccess); - verify(obsClientMock, times(1)).copyObject(anyString(), anyString(), anyString(), anyString()); - } - - @Test - public void deleteTenant() { - doNothing().when(obsOperator).deleteTenantCode(anyString()); - try { - obsOperator.deleteTenant(TENANT_CODE_MOCK); - } catch (Exception e) { - Assertions.fail("unexpected exception caught in unit test"); - } - - verify(obsOperator, times(1)).deleteTenantCode(anyString()); - } - - @Test - public void getObsResDir() { - final String expectedObsResDir = String.format("dolphinscheduler/%s/resources", TENANT_CODE_MOCK); - final String obsResDir = obsOperator.getObsResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedObsResDir, obsResDir); - } - - @Test - public void getObsUdfDir() { - final String expectedObsUdfDir = String.format("dolphinscheduler/%s/udfs", TENANT_CODE_MOCK); - final String obsUdfDir = obsOperator.getObsUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedObsUdfDir, obsUdfDir); - } - - @Test - public void getObsTenantDir() { - final String expectedObsTenantDir = String.format(FORMAT_S_S, DIR_MOCK, TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(obsOperator).getObsDataBasePath(); - final String obsTenantDir = obsOperator.getObsTenantDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedObsTenantDir, obsTenantDir); - } - - @Test - public void deleteDir() { - doReturn(true).when(obsClientMock).doesObjectExist(anyString(), anyString()); - obsOperator.deleteDir(DIR_MOCK); - verify(obsClientMock, times(1)).deleteObject(anyString(), anyString()); - } - - @Test - public void testGetFileStatus() throws Exception { - StorageEntity entity = obsOperator.getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(FULL_NAME, entity.getFullName()); - Assertions.assertEquals("dir1/", entity.getFileName()); - } - - @Test - public void testListFilesStatus() throws Exception { - List result = - obsOperator.listFilesStatus("dolphinscheduler/default/resources/", - "dolphinscheduler/default/resources/", - "default", ResourceType.FILE); - Assertions.assertEquals(0, result.size()); - } - - @Test - public void testListFilesStatusRecursively() throws Exception { - StorageEntity entity = new StorageEntity(); - entity.setFullName(FULL_NAME); - - doReturn(entity).when(obsOperator).getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - doReturn(Collections.EMPTY_LIST).when(obsOperator).listFilesStatus(anyString(), anyString(), anyString(), - Mockito.any(ResourceType.class)); - - List result = - obsOperator.listFilesStatusRecursively(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(0, result.size()); - } -} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/main/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/main/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperator.java index 61754b52a7..afdc6874c9 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/main/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperator.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/main/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperator.java @@ -17,21 +17,16 @@ package org.apache.dolphinscheduler.plugin.storage.oss; -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_FILE; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_UDF; - import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; import org.apache.dolphinscheduler.common.factory.OssClientFactory; import org.apache.dolphinscheduler.common.model.OssConnection; import org.apache.dolphinscheduler.common.utils.FileUtils; import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.AbstractStorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskConstants; -import org.apache.dolphinscheduler.spi.enums.ResourceType; import org.apache.commons.lang3.StringUtils; @@ -44,23 +39,22 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.Data; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSException; -import com.aliyun.oss.ServiceException; -import com.aliyun.oss.model.DeleteObjectsRequest; +import com.aliyun.oss.model.GetObjectRequest; import com.aliyun.oss.model.ListObjectsV2Request; import com.aliyun.oss.model.ListObjectsV2Result; import com.aliyun.oss.model.OSSObject; @@ -68,9 +62,8 @@ import com.aliyun.oss.model.OSSObjectSummary; import com.aliyun.oss.model.ObjectMetadata; import com.aliyun.oss.model.PutObjectRequest; -@Data @Slf4j -public class OssStorageOperator implements Closeable, StorageOperate { +public class OssStorageOperator extends AbstractStorageOperator implements Closeable, StorageOperator { private String accessKeyId; @@ -86,10 +79,11 @@ public class OssStorageOperator implements Closeable, StorageOperate { private OSS ossClient; - public OssStorageOperator() { + public OssStorageOperator(String resourceBaseAbsolutePath) { + super(resourceBaseAbsolutePath); } - public void init() { + private void init() { this.accessKeyId = readOssAccessKeyID(); this.accessKeySecret = readOssAccessKeySecret(); this.endPoint = readOssEndPoint(); @@ -108,7 +102,7 @@ public class OssStorageOperator implements Closeable, StorageOperate { this.region = readOssRegion(); this.bucketName = readOssBucketName(); this.ossConnection = ossConnection; - this.ossClient = getOssClient(); + this.ossClient = buildOssClient(); ensureBucketSuccessfullyCreated(bucketName); } @@ -136,80 +130,44 @@ public class OssStorageOperator implements Closeable, StorageOperate { return new OssConnection(accessKeyId, accessKeySecret, endPoint); } + @Override + public String getStorageBaseDirectory() { + // All directory should end with File.separator + if (resourceBaseAbsolutePath.startsWith("/")) { + log.warn("{} -> {} should not start with / in Oss", Constants.RESOURCE_UPLOAD_PATH, + resourceBaseAbsolutePath); + return resourceBaseAbsolutePath.substring(1); + } + return resourceBaseAbsolutePath; + } + @Override public void close() throws IOException { ossClient.shutdown(); } + @SneakyThrows @Override - public void createTenantDirIfNotExists(String tenantCode) throws Exception { - mkdir(tenantCode, getOssResDir(tenantCode)); - mkdir(tenantCode, getOssUdfDir(tenantCode)); - } + public void createStorageDir(String directory) { + directory = transformAbsolutePathToOssKey(directory); - @Override - public String getResDir(String tenantCode) { - return getOssResDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public String getUdfDir(String tenantCode) { - return getOssUdfDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public boolean mkdir(String tenantCode, String path) throws IOException { - final String key = path + FOLDER_SEPARATOR; - if (!ossClient.doesObjectExist(bucketName, key)) { - createOssPrefix(bucketName, key); + if (ossClient.doesObjectExist(bucketName, directory)) { + throw new FileAlreadyExistsException("directory: " + directory + " already exists"); } - return true; - } - - protected void createOssPrefix(final String bucketName, final String key) { ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentLength(0); InputStream emptyContent = new ByteArrayInputStream(new byte[0]); - PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, emptyContent, metadata); + PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, directory, emptyContent, metadata); ossClient.putObject(putObjectRequest); } + @SneakyThrows @Override - public String getResourceFullName(String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return String.format(FORMAT_S_S, getOssResDir(tenantCode), fileName); - } + public void download(String srcFilePath, + String dstFilePath, + boolean overwrite) { + srcFilePath = transformAbsolutePathToOssKey(srcFilePath); - @Override - public String getFileName(ResourceType resourceType, String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return getDir(resourceType, tenantCode) + fileName; - } - - @Override - public boolean delete(String fullName, List childrenPathList, boolean recursive) throws IOException { - // append the resource fullName to the list for deletion. - childrenPathList.add(fullName); - - DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName) - .withKeys(childrenPathList); - try { - ossClient.deleteObjects(deleteObjectsRequest); - } catch (Exception e) { - log.error("delete objects error", e); - return false; - } - - return true; - } - - @Override - public void download(String srcFilePath, String dstFilePath, - boolean overwrite) throws IOException { File dstFile = new File(dstFilePath); if (dstFile.isDirectory()) { Files.delete(dstFile.toPath()); @@ -234,268 +192,118 @@ public class OssStorageOperator implements Closeable, StorageOperate { } @Override - public boolean exists(String fileName) throws IOException { + public boolean exists(String fileName) { + fileName = transformAbsolutePathToOssKey(fileName); return ossClient.doesObjectExist(bucketName, fileName); } @Override - public boolean delete(String filePath, boolean recursive) throws IOException { - try { - ossClient.deleteObject(bucketName, filePath); - return true; - } catch (OSSException e) { - log.error("fail to delete the object, the resource path is {}", filePath, e); - return false; - } + public void delete(String filePath, boolean recursive) { + filePath = transformAbsolutePathToOssKey(filePath); + ossClient.deleteObject(bucketName, filePath); } @Override - public boolean copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) throws IOException { + public void copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) { + srcPath = transformAbsolutePathToOssKey(srcPath); + dstPath = transformAbsolutePathToOssKey(dstPath); + ossClient.copyObject(bucketName, srcPath, bucketName, dstPath); if (deleteSource) { ossClient.deleteObject(bucketName, srcPath); } - return true; } + @SneakyThrows @Override - public String getDir(ResourceType resourceType, String tenantCode) { - switch (resourceType) { - case UDF: - return getUdfDir(tenantCode); - case FILE: - return getResDir(tenantCode); - case ALL: - return getOssDataBasePath(); - default: - return ""; - } - } - - @Override - public boolean upload(String tenantCode, String srcFile, String dstPath, boolean deleteSource, - boolean overwrite) throws IOException { - try { - ossClient.putObject(bucketName, dstPath, new File(srcFile)); - if (deleteSource) { - Files.delete(Paths.get(srcFile)); + public void upload(String srcFile, String dstPath, boolean deleteSource, boolean overwrite) { + dstPath = transformAbsolutePathToOssKey(dstPath); + if (ossClient.doesObjectExist(bucketName, dstPath)) { + if (!overwrite) { + throw new FileAlreadyExistsException("file: " + dstPath + " already exists"); + } else { + ossClient.deleteObject(bucketName, dstPath); } - return true; - } catch (OSSException e) { - log.error("upload failed, the bucketName is {}, the filePath is {}", bucketName, dstPath, e); - return false; + + } + ossClient.putObject(bucketName, dstPath, new File(srcFile)); + if (deleteSource) { + Files.delete(Paths.get(srcFile)); } } + @SneakyThrows @Override - public List vimFile(String tenantCode, String filePath, int skipLineNums, int limit) throws IOException { - if (StringUtils.isBlank(filePath)) { - log.error("file path:{} is empty", filePath); - return Collections.emptyList(); - } + public List fetchFileContent(String filePath, int skipLineNums, int limit) { + filePath = transformAbsolutePathToOssKey(filePath); OSSObject ossObject = ossClient.getObject(bucketName, filePath); - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()))) { - Stream stream = bufferedReader.lines().skip(skipLineNums).limit(limit); - return stream.collect(Collectors.toList()); + try ( + InputStreamReader inputStreamReader = new InputStreamReader(ossObject.getObjectContent()); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + return bufferedReader.lines() + .skip(skipLineNums) + .limit(limit) + .collect(Collectors.toList()); } } @Override - public ResUploadType returnStorageType() { - return ResUploadType.OSS; + public List listStorageEntity(String resourceAbsolutePath) { + final String ossResourceAbsolutePath = transformAbsolutePathToOssKey(resourceAbsolutePath); + + ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request() + .withBucketName(bucketName) + .withDelimiter("/") + .withPrefix(ossResourceAbsolutePath); + + ListObjectsV2Result listObjectsV2Result = ossClient.listObjectsV2(listObjectsV2Request); + List storageEntities = new ArrayList<>(); + storageEntities.addAll(listObjectsV2Result.getCommonPrefixes() + .stream() + .map(this::transformCommonPrefixToStorageEntity) + .collect(Collectors.toList())); + storageEntities.addAll( + listObjectsV2Result.getObjectSummaries().stream() + .filter(s3ObjectSummary -> !s3ObjectSummary.getKey().equals(resourceAbsolutePath)) + .map(this::transformOSSObjectToStorageEntity) + .collect(Collectors.toList())); + + return storageEntities; + } @Override - public List listFilesStatusRecursively(String path, String defaultPath, String tenantCode, - ResourceType type) { + public List listFileStorageEntityRecursively(String resourceAbsolutePath) { + resourceAbsolutePath = transformOssKeyToAbsolutePath(resourceAbsolutePath); + + Set visited = new HashSet<>(); List storageEntityList = new ArrayList<>(); - LinkedList foldersToFetch = new LinkedList<>(); - - StorageEntity initialEntity = null; - try { - initialEntity = getFileStatus(path, defaultPath, tenantCode, type); - } catch (Exception e) { - log.error("error while listing files status recursively, path: {}", path, e); - return storageEntityList; - } - foldersToFetch.add(initialEntity); + LinkedList foldersToFetch = new LinkedList<>(); + foldersToFetch.addLast(resourceAbsolutePath); while (!foldersToFetch.isEmpty()) { - String pathToExplore = foldersToFetch.pop().getFullName(); - try { - List tempList = listFilesStatus(pathToExplore, defaultPath, tenantCode, type); - for (StorageEntity temp : tempList) { - if (temp.isDirectory()) { - foldersToFetch.add(temp); + String pathToExplore = foldersToFetch.pop(); + visited.add(pathToExplore); + List tempList = listStorageEntity(pathToExplore); + for (StorageEntity temp : tempList) { + if (temp.isDirectory()) { + if (visited.contains(temp.getFullName())) { + continue; } + foldersToFetch.add(temp.getFullName()); } - storageEntityList.addAll(tempList); - } catch (Exception e) { - log.error("error while listing files stat:wus recursively, path: {}", pathToExplore, e); } + storageEntityList.addAll(tempList); } return storageEntityList; } @Override - public List listFilesStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception { - List storageEntityList = new ArrayList<>(); - - ListObjectsV2Result result = null; - String nextContinuationToken = null; - do { - try { - ListObjectsV2Request request = new ListObjectsV2Request(); - request.setBucketName(bucketName); - request.setPrefix(path); - request.setDelimiter(FOLDER_SEPARATOR); - request.setContinuationToken(nextContinuationToken); - - result = ossClient.listObjectsV2(request); - } catch (Exception e) { - throw new ServiceException("Get OSS file list exception", e); - } - - List summaries = result.getObjectSummaries(); - - for (OSSObjectSummary summary : summaries) { - if (!summary.getKey().endsWith(FOLDER_SEPARATOR)) { - // the path is a file - String[] aliasArr = summary.getKey().split(FOLDER_SEPARATOR); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, summary.getKey()); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(summary.getKey()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(summary.getSize()); - entity.setCreateTime(summary.getLastModified()); - entity.setUpdateTime(summary.getLastModified()); - entity.setPfullName(path); - - storageEntityList.add(entity); - } - } - - for (String commonPrefix : result.getCommonPrefixes()) { - // the paths in commonPrefix are directories - String suffix = StringUtils.difference(path, commonPrefix); - String fileName = StringUtils.difference(defaultPath, commonPrefix); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(suffix); - entity.setFileName(fileName); - entity.setFullName(commonPrefix); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - entity.setCreateTime(null); - entity.setUpdateTime(null); - entity.setPfullName(path); - - storageEntityList.add(entity); - } - - nextContinuationToken = result.getNextContinuationToken(); - } while (result.isTruncated()); - - return storageEntityList; - } - - @Override - public StorageEntity getFileStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws Exception { - ListObjectsV2Request request = new ListObjectsV2Request(); - request.setBucketName(bucketName); - request.setPrefix(path); - request.setDelimiter(FOLDER_SEPARATOR); - - ListObjectsV2Result result; - try { - result = ossClient.listObjectsV2(request); - } catch (Exception e) { - throw new ServiceException("Get OSS file list exception", e); - } - - List summaries = result.getObjectSummaries(); - - if (path.endsWith(FOLDER_SEPARATOR)) { - // the path is a directory that may or may not exist in OSS - String alias = findDirAlias(path); - String fileName = StringUtils.difference(defaultPath, path); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(path); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - - return entity; - - } else { - // the path is a file - if (summaries.size() > 0) { - OSSObjectSummary summary = summaries.get(0); - String[] aliasArr = summary.getKey().split(FOLDER_SEPARATOR); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, summary.getKey()); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(summary.getKey()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(summary.getSize()); - entity.setCreateTime(summary.getLastModified()); - entity.setUpdateTime(summary.getLastModified()); - - return entity; - } - } - - throw new FileNotFoundException("Object is not found in OSS Bucket: " + bucketName); - } - - @Override - public void deleteTenant(String tenantCode) throws Exception { - deleteTenantCode(tenantCode); - } - - public String getOssResDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_FILE, getOssTenantDir(tenantCode)); - } - - public String getOssUdfDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_UDF, getOssTenantDir(tenantCode)); - } - - public String getOssTenantDir(String tenantCode) { - return String.format(FORMAT_S_S, getOssDataBasePath(), tenantCode); - } - - public String getOssDataBasePath() { - if (FOLDER_SEPARATOR.equals(RESOURCE_UPLOAD_PATH)) { - return ""; - } else { - return RESOURCE_UPLOAD_PATH.replaceFirst(FOLDER_SEPARATOR, ""); - } - } - - protected void deleteTenantCode(String tenantCode) { - deleteDir(getResDir(tenantCode)); - deleteDir(getUdfDir(tenantCode)); + public StorageEntity getStorageEntity(String resourceAbsolutePath) { + resourceAbsolutePath = transformAbsolutePathToOssKey(resourceAbsolutePath); + OSSObject object = ossClient.getObject(new GetObjectRequest(bucketName, resourceAbsolutePath)); + return transformOSSObjectToStorageEntity(object); } public void ensureBucketSuccessfullyCreated(String bucketName) { @@ -512,22 +320,70 @@ public class OssStorageOperator implements Closeable, StorageOperate { log.info("bucketName: {} has been found, the current regionName is {}", bucketName, region); } - protected void deleteDir(String directoryName) { - if (ossClient.doesObjectExist(bucketName, directoryName)) { - ossClient.deleteObject(bucketName, directoryName); - } - } - protected OSS buildOssClient() { return OssClientFactory.buildOssClient(ossConnection); } - private String findDirAlias(String dirPath) { - if (!dirPath.endsWith(FOLDER_SEPARATOR)) { - return dirPath; - } + protected StorageEntity transformOSSObjectToStorageEntity(OSSObject ossObject) { + ResourceMetadata resourceMetaData = getResourceMetaData(ossObject.getKey()); - Path path = Paths.get(dirPath); - return path.getName(path.getNameCount() - 1) + FOLDER_SEPARATOR; + StorageEntity storageEntity = new StorageEntity(); + storageEntity.setFileName(new File(ossObject.getKey()).getName()); + storageEntity.setFullName(ossObject.getKey()); + storageEntity.setPfullName(resourceMetaData.getResourceParentAbsolutePath()); + storageEntity.setType(resourceMetaData.getResourceType()); + storageEntity.setDirectory(resourceMetaData.isDirectory()); + storageEntity.setSize(ossObject.getObjectMetadata().getContentLength()); + storageEntity.setCreateTime(ossObject.getObjectMetadata().getLastModified()); + storageEntity.setUpdateTime(ossObject.getObjectMetadata().getLastModified()); + return storageEntity; + } + + private StorageEntity transformOSSObjectToStorageEntity(OSSObjectSummary ossObjectSummary) { + String absolutePath = transformOssKeyToAbsolutePath(ossObjectSummary.getKey()); + + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + + StorageEntity storageEntity = new StorageEntity(); + storageEntity.setFileName(new File(absolutePath).getName()); + storageEntity.setFullName(absolutePath); + storageEntity.setPfullName(resourceMetaData.getResourceParentAbsolutePath()); + storageEntity.setType(resourceMetaData.getResourceType()); + storageEntity.setDirectory(resourceMetaData.isDirectory()); + storageEntity.setSize(ossObjectSummary.getSize()); + storageEntity.setCreateTime(ossObjectSummary.getLastModified()); + storageEntity.setUpdateTime(ossObjectSummary.getLastModified()); + return storageEntity; + } + + private StorageEntity transformCommonPrefixToStorageEntity(String commonPrefix) { + String absolutePath = transformOssKeyToAbsolutePath(commonPrefix); + + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + + StorageEntity entity = new StorageEntity(); + entity.setFileName(new File(absolutePath).getName()); + entity.setFullName(absolutePath); + entity.setDirectory(resourceMetaData.isDirectory()); + entity.setType(resourceMetaData.getResourceType()); + entity.setSize(0L); + entity.setCreateTime(null); + entity.setUpdateTime(null); + return entity; + } + + private String transformAbsolutePathToOssKey(String absolutePath) { + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + if (resourceMetaData.isDirectory()) { + return FileUtils.concatFilePath(absolutePath, "/"); + } + return absolutePath; + } + + private String transformOssKeyToAbsolutePath(String s3Key) { + if (s3Key.endsWith("/")) { + return s3Key.substring(0, s3Key.length() - 1); + } + return s3Key; } } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/main/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperatorFactory.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/main/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperatorFactory.java index 783faf59ea..966cc471ab 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/main/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperatorFactory.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/main/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperatorFactory.java @@ -17,20 +17,20 @@ package org.apache.dolphinscheduler.plugin.storage.oss; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperateFactory; +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperatorFactory; import org.apache.dolphinscheduler.plugin.storage.api.StorageType; import com.google.auto.service.AutoService; -@AutoService(StorageOperateFactory.class) -public class OssStorageOperatorFactory implements StorageOperateFactory { +@AutoService(StorageOperatorFactory.class) +public class OssStorageOperatorFactory implements StorageOperatorFactory { @Override - public StorageOperate createStorageOperate() { - OssStorageOperator ossOperator = new OssStorageOperator(); - ossOperator.init(); - return ossOperator; + public StorageOperator createStorageOperate() { + return new OssStorageOperator(PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler")); } @Override diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/test/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperatorTest.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/test/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperatorTest.java deleted file mode 100644 index b84df0a024..0000000000 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-oss/src/test/java/org/apache/dolphinscheduler/plugin/storage/oss/OssStorageOperatorTest.java +++ /dev/null @@ -1,298 +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.dolphinscheduler.plugin.storage.oss; - -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.spi.enums.ResourceType; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.aliyun.oss.OSS; -import com.aliyun.oss.model.ListObjectsV2Request; -import com.aliyun.oss.model.ListObjectsV2Result; - -@ExtendWith(MockitoExtension.class) -public class OssStorageOperatorTest { - - private static final String ACCESS_KEY_ID_MOCK = "ACCESS_KEY_ID_MOCK"; - private static final String ACCESS_KEY_SECRET_MOCK = "ACCESS_KEY_SECRET_MOCK"; - private static final String REGION_MOCK = "REGION_MOCK"; - private static final String END_POINT_MOCK = "END_POINT_MOCK"; - private static final String BUCKET_NAME_MOCK = "BUCKET_NAME_MOCK"; - private static final String TENANT_CODE_MOCK = "TENANT_CODE_MOCK"; - private static final String DIR_MOCK = "DIR_MOCK"; - private static final String FILE_NAME_MOCK = "FILE_NAME_MOCK"; - private static final String FILE_PATH_MOCK = "FILE_PATH_MOCK"; - - private static final String FULL_NAME = "/tmp/dir1/"; - - private static final String DEFAULT_PATH = "/tmp/"; - - @Mock - private OSS ossClientMock; - - private OssStorageOperator ossOperator; - - @BeforeEach - public void setUp() throws Exception { - ossOperator = spy(new OssStorageOperator()); - doReturn(ACCESS_KEY_ID_MOCK).when(ossOperator) - .readOssAccessKeyID(); - doReturn(ACCESS_KEY_SECRET_MOCK).when(ossOperator) - .readOssAccessKeySecret(); - doReturn(REGION_MOCK).when(ossOperator).readOssRegion(); - doReturn(BUCKET_NAME_MOCK).when(ossOperator).readOssBucketName(); - doReturn(END_POINT_MOCK).when(ossOperator).readOssEndPoint(); - doReturn(ossClientMock).when(ossOperator).buildOssClient(); - doNothing().when(ossOperator).ensureBucketSuccessfullyCreated(any()); - - ossOperator.init(); - - } - - @Test - public void initOssOperator() { - verify(ossOperator, times(1)).buildOssClient(); - Assertions.assertEquals(ACCESS_KEY_ID_MOCK, ossOperator.getAccessKeyId()); - Assertions.assertEquals(ACCESS_KEY_SECRET_MOCK, ossOperator.getAccessKeySecret()); - Assertions.assertEquals(REGION_MOCK, ossOperator.getRegion()); - Assertions.assertEquals(BUCKET_NAME_MOCK, ossOperator.getBucketName()); - } - - @Test - public void tearDownOssOperator() throws IOException { - doNothing().when(ossClientMock).shutdown(); - ossOperator.close(); - verify(ossClientMock, times(1)).shutdown(); - } - - @Test - public void createTenantResAndUdfDir() throws Exception { - doReturn(DIR_MOCK).when(ossOperator).getOssResDir(TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(ossOperator).getOssUdfDir(TENANT_CODE_MOCK); - doReturn(true).when(ossOperator).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - ossOperator.createTenantDirIfNotExists(TENANT_CODE_MOCK); - verify(ossOperator, times(2)).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - } - - @Test - public void getResDir() { - final String expectedResourceDir = String.format("dolphinscheduler/%s/resources/", TENANT_CODE_MOCK); - final String dir = ossOperator.getResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedResourceDir, dir); - } - - @Test - public void getUdfDir() { - final String expectedUdfDir = String.format("dolphinscheduler/%s/udfs/", TENANT_CODE_MOCK); - final String dir = ossOperator.getUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedUdfDir, dir); - } - - @Test - public void mkdirWhenDirExists() { - boolean isSuccess = false; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - doReturn(true).when(ossClientMock).doesObjectExist(BUCKET_NAME_MOCK, key); - isSuccess = ossOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - verify(ossClientMock, times(1)).doesObjectExist(BUCKET_NAME_MOCK, key); - - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); - } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void mkdirWhenDirNotExists() { - boolean isSuccess = true; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - doReturn(false).when(ossClientMock).doesObjectExist(BUCKET_NAME_MOCK, key); - doNothing().when(ossOperator).createOssPrefix(BUCKET_NAME_MOCK, key); - isSuccess = ossOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - verify(ossClientMock, times(1)).doesObjectExist(BUCKET_NAME_MOCK, key); - verify(ossOperator, times(1)).createOssPrefix(BUCKET_NAME_MOCK, key); - - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); - } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void getResourceFullName() { - final String expectedResourceFullName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFullName = ossOperator.getResourceFullName(TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedResourceFullName, resourceFullName); - } - - @Test - public void getResourceFileName() { - final String expectedResourceFileName = FILE_NAME_MOCK; - final String resourceFullName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFileName = ossOperator.getResourceFileName(TENANT_CODE_MOCK, resourceFullName); - Assertions.assertEquals(expectedResourceFileName, resourceFileName); - } - - @Test - public void getFileName() { - final String expectedFileName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String fileName = ossOperator.getFileName(ResourceType.FILE, TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedFileName, fileName); - } - - @Test - public void exists() { - boolean doesExist = false; - doReturn(true).when(ossClientMock).doesObjectExist(BUCKET_NAME_MOCK, FILE_NAME_MOCK); - try { - doesExist = ossOperator.exists(FILE_NAME_MOCK); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(doesExist); - verify(ossClientMock, times(1)).doesObjectExist(BUCKET_NAME_MOCK, FILE_NAME_MOCK); - } - - @Test - public void delete() { - boolean isDeleted = false; - doReturn(null).when(ossClientMock).deleteObject(anyString(), anyString()); - try { - isDeleted = ossOperator.delete(FILE_NAME_MOCK, true); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isDeleted); - verify(ossClientMock, times(1)).deleteObject(anyString(), anyString()); - } - - @Test - public void copy() { - boolean isSuccess = false; - doReturn(null).when(ossClientMock).copyObject(anyString(), anyString(), anyString(), anyString()); - try { - isSuccess = ossOperator.copy(FILE_PATH_MOCK, FILE_PATH_MOCK, false, false); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isSuccess); - verify(ossClientMock, times(1)).copyObject(anyString(), anyString(), anyString(), anyString()); - } - - @Test - public void deleteTenant() { - doNothing().when(ossOperator).deleteTenantCode(anyString()); - try { - ossOperator.deleteTenant(TENANT_CODE_MOCK); - } catch (Exception e) { - Assertions.fail("unexpected exception caught in unit test"); - } - - verify(ossOperator, times(1)).deleteTenantCode(anyString()); - } - - @Test - public void getOssResDir() { - final String expectedOssResDir = String.format("dolphinscheduler/%s/resources", TENANT_CODE_MOCK); - final String ossResDir = ossOperator.getOssResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedOssResDir, ossResDir); - } - - @Test - public void getOssUdfDir() { - final String expectedOssUdfDir = String.format("dolphinscheduler/%s/udfs", TENANT_CODE_MOCK); - final String ossUdfDir = ossOperator.getOssUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedOssUdfDir, ossUdfDir); - } - - @Test - public void getOssTenantDir() { - final String expectedOssTenantDir = String.format(FORMAT_S_S, DIR_MOCK, TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(ossOperator).getOssDataBasePath(); - final String ossTenantDir = ossOperator.getOssTenantDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedOssTenantDir, ossTenantDir); - } - - @Test - public void deleteDir() { - doReturn(true).when(ossClientMock).doesObjectExist(anyString(), anyString()); - ossOperator.deleteDir(DIR_MOCK); - verify(ossClientMock, times(1)).deleteObject(anyString(), anyString()); - } - - @Test - public void testGetFileStatus() throws Exception { - doReturn(new ListObjectsV2Result()).when(ossClientMock).listObjectsV2(Mockito.any(ListObjectsV2Request.class)); - StorageEntity entity = ossOperator.getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(FULL_NAME, entity.getFullName()); - Assertions.assertEquals("dir1/", entity.getFileName()); - } - - @Test - public void testListFilesStatus() throws Exception { - doReturn(new ListObjectsV2Result()).when(ossClientMock).listObjectsV2(Mockito.any(ListObjectsV2Request.class)); - List result = - ossOperator.listFilesStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(0, result.size()); - } - - @Test - public void testListFilesStatusRecursively() throws Exception { - StorageEntity entity = new StorageEntity(); - entity.setFullName(FULL_NAME); - - doReturn(entity).when(ossOperator).getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - doReturn(Collections.EMPTY_LIST).when(ossOperator).listFilesStatus(anyString(), anyString(), anyString(), - Mockito.any(ResourceType.class)); - - List result = - ossOperator.listFilesStatusRecursively(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(0, result.size()); - } -} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/pom.xml b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/pom.xml index b9dfd5d1c9..2c7a1d2d99 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/pom.xml +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/pom.xml @@ -42,5 +42,12 @@ com.amazonaws aws-java-sdk-s3 + + + org.testcontainers + minio + test + + diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperator.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperator.java index 3e4b207b50..d70a4c5990 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperator.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperator.java @@ -17,19 +17,13 @@ package org.apache.dolphinscheduler.plugin.storage.s3; -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_FILE; -import static org.apache.dolphinscheduler.common.constants.Constants.RESOURCE_TYPE_UDF; - import org.apache.dolphinscheduler.authentication.aws.AmazonS3ClientFactory; import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.common.enums.ResUploadType; import org.apache.dolphinscheduler.common.utils.FileUtils; -import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.AbstractStorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.spi.enums.ResourceType; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.commons.lang3.StringUtils; @@ -37,28 +31,24 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.Data; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import com.amazonaws.AmazonServiceException; import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.AmazonS3Exception; -import com.amazonaws.services.s3.model.DeleteObjectsRequest; import com.amazonaws.services.s3.model.ListObjectsV2Request; import com.amazonaws.services.s3.model.ListObjectsV2Result; import com.amazonaws.services.s3.model.ObjectMetadata; @@ -66,33 +56,30 @@ import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectInputStream; import com.amazonaws.services.s3.model.S3ObjectSummary; -import com.amazonaws.services.s3.transfer.MultipleFileDownload; -import com.amazonaws.services.s3.transfer.TransferManager; -import com.amazonaws.services.s3.transfer.TransferManagerBuilder; @Slf4j -@Data -public class S3StorageOperator implements Closeable, StorageOperate { +public class S3StorageOperator extends AbstractStorageOperator implements Closeable, StorageOperator { - private String bucketName; + private final String bucketName; - private AmazonS3 s3Client; + private final AmazonS3 s3Client; - public S3StorageOperator() { + public S3StorageOperator(S3StorageProperties s3StorageProperties) { + super(s3StorageProperties.getResourceUploadPath()); + bucketName = s3StorageProperties.getBucketName(); + s3Client = AmazonS3ClientFactory.createAmazonS3Client(s3StorageProperties.getS3Configuration()); + exceptionWhenBucketNameNotExists(bucketName); } - public void init() { - bucketName = readBucketName(); - s3Client = buildS3Client(); - checkBucketNameExists(bucketName); - } - - protected AmazonS3 buildS3Client() { - return AmazonS3ClientFactory.createAmazonS3Client(PropertyUtils.getByPrefix("aws.s3.", "")); - } - - protected String readBucketName() { - return PropertyUtils.getString(Constants.AWS_S3_BUCKET_NAME); + @Override + public String getStorageBaseDirectory() { + // All directory should end with File.separator + if (resourceBaseAbsolutePath.startsWith("/")) { + log.warn("{} -> {} should not start with / in s3", Constants.RESOURCE_UPLOAD_PATH, + resourceBaseAbsolutePath); + return resourceBaseAbsolutePath.substring(1); + } + return resourceBaseAbsolutePath; } @Override @@ -100,54 +87,28 @@ public class S3StorageOperator implements Closeable, StorageOperate { s3Client.shutdown(); } + @SneakyThrows @Override - public void createTenantDirIfNotExists(String tenantCode) throws Exception { - mkdir(tenantCode, getS3ResDir(tenantCode)); - mkdir(tenantCode, getS3UdfDir(tenantCode)); - } - - @Override - public String getResDir(String tenantCode) { - return getS3ResDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public String getUdfDir(String tenantCode) { - return getS3UdfDir(tenantCode) + FOLDER_SEPARATOR; - } - - @Override - public boolean mkdir(String tenantCode, String path) throws IOException { - String objectName = path + FOLDER_SEPARATOR; - if (!s3Client.doesObjectExist(bucketName, objectName)) { - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentLength(0); - InputStream emptyContent = new ByteArrayInputStream(new byte[0]); - PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, emptyContent, metadata); - s3Client.putObject(putObjectRequest); + public void createStorageDir(String directoryAbsolutePath) { + directoryAbsolutePath = transformAbsolutePathToS3Key(directoryAbsolutePath); + if (s3Client.doesObjectExist(bucketName, directoryAbsolutePath)) { + throw new FileAlreadyExistsException( + "The directory " + directoryAbsolutePath + " already exists in the bucket " + bucketName); } - return true; + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(0); + InputStream emptyContent = new ByteArrayInputStream(new byte[0]); + PutObjectRequest putObjectRequest = + new PutObjectRequest(bucketName, directoryAbsolutePath, emptyContent, metadata); + s3Client.putObject(putObjectRequest); } + @SneakyThrows @Override - public String getResourceFullName(String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return String.format(FORMAT_S_S, getS3ResDir(tenantCode), fileName); - } - - @Override - public String getFileName(ResourceType resourceType, String tenantCode, String fileName) { - if (fileName.startsWith(FOLDER_SEPARATOR)) { - fileName = fileName.replaceFirst(FOLDER_SEPARATOR, ""); - } - return getDir(resourceType, tenantCode) + fileName; - } - - @Override - public void download(String srcFilePath, String dstFilePath, - boolean overwrite) throws IOException { + public void download(String srcFilePath, + String dstFilePath, + boolean overwrite) { + srcFilePath = transformAbsolutePathToS3Key(srcFilePath); File dstFile = new File(dstFilePath); if (dstFile.isDirectory()) { Files.delete(dstFile.toPath()); @@ -163,187 +124,84 @@ public class S3StorageOperator implements Closeable, StorageOperate { while ((readLen = s3is.read(readBuf)) > 0) { fos.write(readBuf, 0, readLen); } - } catch (AmazonServiceException e) { - throw new IOException(e.getMessage()); - } catch (FileNotFoundException e) { - log.error("the destination file {} not found", dstFilePath); - throw e; } } @Override - public boolean exists(String fullName) throws IOException { + public boolean exists(String fullName) { + fullName = transformAbsolutePathToS3Key(fullName); return s3Client.doesObjectExist(bucketName, fullName); } @Override - public boolean delete(String fullName, boolean recursive) throws IOException { - try { - s3Client.deleteObject(bucketName, fullName); - return true; - } catch (AmazonServiceException e) { - log.error("delete the object error,the resource path is {}", fullName); - return false; + public void delete(String absolutePath, boolean recursive) { + absolutePath = transformAbsolutePathToS3Key(absolutePath); + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + if (!resourceMetaData.isDirectory()) { + s3Client.deleteObject(bucketName, absolutePath); + return; } + if (recursive) { + List storageEntities = listStorageEntityRecursively(absolutePath); + for (StorageEntity storageEntity : storageEntities) { + s3Client.deleteObject(bucketName, transformAbsolutePathToS3Key(storageEntity.getFullName())); + } + } + s3Client.deleteObject(bucketName, absolutePath); } @Override - public boolean delete(String fullName, List childrenPathList, boolean recursive) throws IOException { - // append the resource fullName to the list for deletion. - childrenPathList.add(fullName); + public void copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) { + srcPath = transformAbsolutePathToS3Key(srcPath); + dstPath = transformAbsolutePathToS3Key(dstPath); - DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName) - .withKeys(childrenPathList.stream().toArray(String[]::new)); - try { - s3Client.deleteObjects(deleteObjectsRequest); - } catch (AmazonServiceException e) { - log.error("delete objects error", e); - return false; + ResourceMetadata resourceMetaData = getResourceMetaData(srcPath); + if (resourceMetaData.isDirectory()) { + throw new UnsupportedOperationException("S3 does not support copying directories."); } - - return true; - } - - @Override - public boolean copy(String srcPath, String dstPath, boolean deleteSource, boolean overwrite) throws IOException { s3Client.copyObject(bucketName, srcPath, bucketName, dstPath); if (deleteSource) { s3Client.deleteObject(bucketName, srcPath); } - return true; } + @SneakyThrows @Override - public String getDir(ResourceType resourceType, String tenantCode) { - switch (resourceType) { - case UDF: - return getUdfDir(tenantCode); - case FILE: - return getResDir(tenantCode); - case ALL: - return getS3DataBasePath(); - default: - return ""; - } + public void upload(String srcFile, String dstPath, boolean deleteSource, boolean overwrite) { + dstPath = transformAbsolutePathToS3Key(dstPath); - } - - @Override - public boolean upload(String tenantCode, String srcFile, String dstPath, boolean deleteSource, - boolean overwrite) throws IOException { - try { - s3Client.putObject(bucketName, dstPath, new File(srcFile)); - - if (deleteSource) { - Files.delete(Paths.get(srcFile)); + if (s3Client.doesObjectExist(bucketName, dstPath)) { + if (overwrite) { + s3Client.deleteObject(bucketName, dstPath); + } else { + throw new FileAlreadyExistsException("The file " + dstPath + " already exists in the bucket " + + bucketName + " and overwrite is not allowed."); } - return true; - } catch (AmazonServiceException e) { - log.error("upload failed,the bucketName is {},the filePath is {}", bucketName, dstPath); - return false; + } + + s3Client.putObject(bucketName, dstPath, new File(srcFile)); + + if (deleteSource) { + Files.delete(Paths.get(srcFile)); } } + @SneakyThrows @Override - public List vimFile(String tenantCode, String filePath, int skipLineNums, int limit) throws IOException { - if (StringUtils.isBlank(filePath)) { - log.error("file path:{} is blank", filePath); - return Collections.emptyList(); - } + public List fetchFileContent(String filePath, int skipLineNums, int limit) { + filePath = transformAbsolutePathToS3Key(filePath); S3Object s3Object = s3Client.getObject(bucketName, filePath); - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(s3Object.getObjectContent()))) { - Stream stream = bufferedReader.lines().skip(skipLineNums).limit(limit); - return stream.collect(Collectors.toList()); + try ( + InputStreamReader inputStreamReader = new InputStreamReader(s3Object.getObjectContent()); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + return bufferedReader.lines() + .skip(skipLineNums) + .limit(limit) + .collect(Collectors.toList()); } } - @Override - public void deleteTenant(String tenantCode) throws Exception { - deleteTenantCode(tenantCode); - } - - /** - * S3 resource dir - * - * @param tenantCode tenant code - * @return S3 resource dir - */ - public String getS3ResDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_FILE, getS3TenantDir(tenantCode)); - } - - /** - * S3 udf dir - * - * @param tenantCode tenant code - * @return get udf dir on S3 - */ - public String getS3UdfDir(String tenantCode) { - return String.format("%s/" + RESOURCE_TYPE_UDF, getS3TenantDir(tenantCode)); - } - - /** - * @param tenantCode tenant code - * @return file directory of tenants on S3 - */ - public String getS3TenantDir(String tenantCode) { - return String.format(FORMAT_S_S, getS3DataBasePath(), tenantCode); - } - - /** - * get data S3 path - * - * @return data S3 path - */ - public String getS3DataBasePath() { - if (FOLDER_SEPARATOR.equals(RESOURCE_UPLOAD_PATH)) { - return ""; - } else { - return RESOURCE_UPLOAD_PATH.replaceFirst(FOLDER_SEPARATOR, ""); - } - } - - protected void deleteTenantCode(String tenantCode) { - deleteDir(getResDir(tenantCode)); - deleteDir(getUdfDir(tenantCode)); - } - - /** - * xxx untest - * upload local directory to S3 - * - * @param tenantCode - * @param keyPrefix the name of directory - * @param strPath - */ - private void uploadDirectory(String tenantCode, String keyPrefix, String strPath) { - s3Client.putObject(bucketName, tenantCode + FOLDER_SEPARATOR + keyPrefix, new File(strPath)); - } - - /** - * xxx untest - * download S3 Directory to local - * - * @param tenantCode - * @param keyPrefix the name of directory - * @param srcPath - */ - private void downloadDirectory(String tenantCode, String keyPrefix, String srcPath) { - TransferManager tm = TransferManagerBuilder.standard().withS3Client(s3Client).build(); - try { - MultipleFileDownload download = - tm.downloadDirectory(bucketName, tenantCode + FOLDER_SEPARATOR + keyPrefix, new File(srcPath)); - download.waitForCompletion(); - } catch (AmazonS3Exception | InterruptedException e) { - log.error("download the directory failed with the bucketName is {} and the keyPrefix is {}", bucketName, - tenantCode + FOLDER_SEPARATOR + keyPrefix); - Thread.currentThread().interrupt(); - } finally { - tm.shutdownNow(); - } - } - - public void checkBucketNameExists(String bucketName) { + void exceptionWhenBucketNameNotExists(String bucketName) { if (StringUtils.isBlank(bucketName)) { throw new IllegalArgumentException("resource.aws.s3.bucket.name is blank"); } @@ -358,199 +216,135 @@ public class S3StorageOperator implements Closeable, StorageOperate { s3Client.getRegionName()); } - /** - * only delete the object of directory ,it`s better to delete the files in it -r - */ - protected void deleteDir(String directoryName) { - if (s3Client.doesObjectExist(bucketName, directoryName)) { - s3Client.deleteObject(bucketName, directoryName); - } + @Override + public List listStorageEntity(String resourceAbsolutePath) { + final String s3ResourceAbsolutePath = transformAbsolutePathToS3Key(resourceAbsolutePath); + ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request() + .withBucketName(bucketName) + .withDelimiter("/") + .withPrefix(s3ResourceAbsolutePath); + + ListObjectsV2Result listObjectsV2Result = s3Client.listObjectsV2(listObjectsV2Request); + List storageEntities = new ArrayList<>(); + storageEntities.addAll(listObjectsV2Result.getCommonPrefixes() + .stream() + .map(this::transformCommonPrefixToStorageEntity) + .collect(Collectors.toList())); + storageEntities.addAll( + listObjectsV2Result.getObjectSummaries().stream() + .filter(s3ObjectSummary -> !s3ObjectSummary.getKey().equals(s3ResourceAbsolutePath)) + .map(this::transformS3ObjectToStorageEntity) + .collect(Collectors.toList())); + + return storageEntities; } @Override - public ResUploadType returnStorageType() { - return ResUploadType.S3; + public List listFileStorageEntityRecursively(String resourceAbsolutePath) { + return listStorageEntityRecursively(resourceAbsolutePath) + .stream() + .filter(storageEntity -> !storageEntity.isDirectory()) + .collect(Collectors.toList()); } @Override - public List listFilesStatusRecursively(String path, String defaultPath, String tenantCode, - ResourceType type) { + public StorageEntity getStorageEntity(String resourceAbsolutePath) { + resourceAbsolutePath = transformAbsolutePathToS3Key(resourceAbsolutePath); + + S3Object object = s3Client.getObject(bucketName, resourceAbsolutePath); + return transformS3ObjectToStorageEntity(object); + } + + private List listStorageEntityRecursively(String resourceAbsolutePath) { + resourceAbsolutePath = transformAbsolutePathToS3Key(resourceAbsolutePath); + + Set visited = new HashSet<>(); List storageEntityList = new ArrayList<>(); - LinkedList foldersToFetch = new LinkedList<>(); - - StorageEntity initialEntity = null; - try { - initialEntity = getFileStatus(path, defaultPath, tenantCode, type); - } catch (Exception e) { - log.error("error while listing files status recursively, path: {}", path, e); - return storageEntityList; - } - foldersToFetch.add(initialEntity); + LinkedList foldersToFetch = new LinkedList<>(); + foldersToFetch.addLast(resourceAbsolutePath); while (!foldersToFetch.isEmpty()) { - String pathToExplore = foldersToFetch.pop().getFullName(); - try { - List tempList = listFilesStatus(pathToExplore, defaultPath, tenantCode, type); - for (StorageEntity temp : tempList) { - if (temp.isDirectory()) { - foldersToFetch.add(temp); + String pathToExplore = foldersToFetch.pop(); + visited.add(pathToExplore); + List tempList = listStorageEntity(pathToExplore); + for (StorageEntity temp : tempList) { + if (temp.isDirectory()) { + if (visited.contains(temp.getFullName())) { + continue; } + foldersToFetch.add(temp.getFullName()); } - storageEntityList.addAll(tempList); - } catch (Exception e) { - log.error("error while listing files status recursively, path: {}", pathToExplore, e); } + storageEntityList.addAll(tempList); } - return storageEntityList; - - } - - @Override - public List listFilesStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws AmazonServiceException { - List storageEntityList = new ArrayList<>(); - - // TODO: optimize pagination - ListObjectsV2Request request = new ListObjectsV2Request(); - request.setBucketName(bucketName); - request.setPrefix(path); - request.setDelimiter("/"); - - ListObjectsV2Result v2Result; - do { - try { - v2Result = s3Client.listObjectsV2(request); - } catch (AmazonServiceException e) { - throw new AmazonServiceException("Get S3 file list exception, error type:" + e.getErrorType(), e); - } - - List summaries = v2Result.getObjectSummaries(); - - for (S3ObjectSummary summary : summaries) { - if (!summary.getKey().endsWith("/")) { - // the path is a file - String[] aliasArr = summary.getKey().split("/"); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, summary.getKey()); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(summary.getKey()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(summary.getSize()); - entity.setCreateTime(summary.getLastModified()); - entity.setUpdateTime(summary.getLastModified()); - entity.setPfullName(path); - - storageEntityList.add(entity); - } - } - - for (String commonPrefix : v2Result.getCommonPrefixes()) { - // the paths in commonPrefix are directories - String suffix = StringUtils.difference(path, commonPrefix); - String fileName = StringUtils.difference(defaultPath, commonPrefix); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(suffix); - entity.setFileName(fileName); - entity.setFullName(commonPrefix); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - entity.setCreateTime(null); - entity.setUpdateTime(null); - entity.setPfullName(path); - - storageEntityList.add(entity); - } - - request.setContinuationToken(v2Result.getContinuationToken()); - - } while (v2Result.isTruncated()); - return storageEntityList; } - @Override - public StorageEntity getFileStatus(String path, String defaultPath, String tenantCode, - ResourceType type) throws AmazonServiceException, FileNotFoundException { - // Notice: we do not use getObject here because intermediate directories - // may not exist in S3, which can cause getObject to throw exception. - // Since we still want to access it on frontend, this is a workaround using listObjects. + private StorageEntity transformS3ObjectToStorageEntity(S3Object object) { - ListObjectsV2Request request = new ListObjectsV2Request(); - request.setBucketName(bucketName); - request.setPrefix(path); - request.setDelimiter("/"); + String s3Key = object.getKey(); + String absolutePath = transformS3KeyToAbsolutePath(s3Key); - ListObjectsV2Result v2Result; - try { - v2Result = s3Client.listObjectsV2(request); - } catch (AmazonServiceException e) { - throw new AmazonServiceException("Get S3 file list exception, error type:" + e.getErrorType(), e); - } + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); - List summaries = v2Result.getObjectSummaries(); - - if (path.endsWith("/")) { - // the path is a directory that may or may not exist in S3 - String alias = findDirAlias(path); - String fileName = StringUtils.difference(defaultPath, path); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(path); - entity.setDirectory(true); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(0); - - return entity; - - } else { - // the path is a file - if (summaries.size() > 0) { - S3ObjectSummary summary = summaries.get(0); - String[] aliasArr = summary.getKey().split("/"); - String alias = aliasArr[aliasArr.length - 1]; - String fileName = StringUtils.difference(defaultPath, summary.getKey()); - - StorageEntity entity = new StorageEntity(); - entity.setAlias(alias); - entity.setFileName(fileName); - entity.setFullName(summary.getKey()); - entity.setDirectory(false); - entity.setUserName(tenantCode); - entity.setType(type); - entity.setSize(summary.getSize()); - entity.setCreateTime(summary.getLastModified()); - entity.setUpdateTime(summary.getLastModified()); - - return entity; - } - } - - throw new FileNotFoundException("Object is not found in S3 Bucket: " + bucketName); + StorageEntity entity = new StorageEntity(); + entity.setFileName(new File(absolutePath).getName()); + entity.setFullName(absolutePath); + entity.setDirectory(resourceMetaData.isDirectory()); + entity.setType(resourceMetaData.getResourceType()); + entity.setSize(object.getObjectMetadata().getContentLength()); + entity.setCreateTime(object.getObjectMetadata().getLastModified()); + entity.setUpdateTime(object.getObjectMetadata().getLastModified()); + return entity; } - /** - * find alias for directories, NOT for files - * a directory is a path ending with "/" - */ - private String findDirAlias(String myStr) { - if (!myStr.endsWith(FOLDER_SEPARATOR)) { - // Make sure system won't crush down if someone accidentally misuse the function. - return myStr; - } + private StorageEntity transformCommonPrefixToStorageEntity(String commonPrefix) { + String absolutePath = transformS3KeyToAbsolutePath(commonPrefix); - Path path = Paths.get(myStr); - return path.getName(path.getNameCount() - 1) + FOLDER_SEPARATOR; + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + + StorageEntity entity = new StorageEntity(); + entity.setFileName(new File(absolutePath).getName()); + entity.setFullName(absolutePath); + entity.setDirectory(resourceMetaData.isDirectory()); + entity.setType(resourceMetaData.getResourceType()); + entity.setSize(0L); + entity.setCreateTime(null); + entity.setUpdateTime(null); + return entity; } + + private StorageEntity transformS3ObjectToStorageEntity(S3ObjectSummary s3ObjectSummary) { + String absolutePath = transformS3KeyToAbsolutePath(s3ObjectSummary.getKey()); + + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + + StorageEntity entity = new StorageEntity(); + entity.setFileName(new File(absolutePath).getName()); + entity.setFullName(absolutePath); + entity.setPfullName(resourceMetaData.getResourceParentAbsolutePath()); + entity.setDirectory(resourceMetaData.isDirectory()); + entity.setType(resourceMetaData.getResourceType()); + entity.setSize(s3ObjectSummary.getSize()); + entity.setCreateTime(s3ObjectSummary.getLastModified()); + entity.setUpdateTime(s3ObjectSummary.getLastModified()); + return entity; + } + + private String transformAbsolutePathToS3Key(String absolutePath) { + ResourceMetadata resourceMetaData = getResourceMetaData(absolutePath); + if (resourceMetaData.isDirectory()) { + return FileUtils.concatFilePath(absolutePath, "/"); + } + return absolutePath; + } + + private String transformS3KeyToAbsolutePath(String s3Key) { + if (s3Key.endsWith("/")) { + return s3Key.substring(0, s3Key.length() - 1); + } + return s3Key; + } + } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperatorFactory.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperatorFactory.java index e1c3a41743..9b1539a4fb 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperatorFactory.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperatorFactory.java @@ -17,20 +17,29 @@ package org.apache.dolphinscheduler.plugin.storage.s3; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperateFactory; +import org.apache.dolphinscheduler.common.constants.Constants; +import org.apache.dolphinscheduler.common.utils.PropertyUtils; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperatorFactory; import org.apache.dolphinscheduler.plugin.storage.api.StorageType; import com.google.auto.service.AutoService; -@AutoService(StorageOperateFactory.class) -public class S3StorageOperatorFactory implements StorageOperateFactory { +@AutoService(StorageOperatorFactory.class) +public class S3StorageOperatorFactory implements StorageOperatorFactory { @Override - public StorageOperate createStorageOperate() { - S3StorageOperator s3StorageOperator = new S3StorageOperator(); - s3StorageOperator.init(); - return s3StorageOperator; + public StorageOperator createStorageOperate() { + final S3StorageProperties s3StorageProperties = getS3StorageProperties(); + return new S3StorageOperator(s3StorageProperties); + } + + private S3StorageProperties getS3StorageProperties() { + return S3StorageProperties.builder() + .bucketName(PropertyUtils.getString(Constants.AWS_S3_BUCKET_NAME)) + .s3Configuration(PropertyUtils.getByPrefix("aws.s3.", "")) + .resourceUploadPath(PropertyUtils.getString(Constants.RESOURCE_UPLOAD_PATH, "/dolphinscheduler")) + .build(); } @Override diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageProperties.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageProperties.java new file mode 100644 index 0000000000..b7f6f159b4 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/main/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageProperties.java @@ -0,0 +1,38 @@ +/* + * 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.dolphinscheduler.plugin.storage.s3; + +import java.util.Map; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class S3StorageProperties { + + private Map s3Configuration; + + private String bucketName; + + private String resourceUploadPath; +} diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperatorTest.java b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperatorTest.java index c5930d713d..dc635cceb5 100644 --- a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperatorTest.java +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/java/org/apache/dolphinscheduler/plugin/storage/s3/S3StorageOperatorTest.java @@ -17,280 +17,289 @@ package org.apache.dolphinscheduler.plugin.storage.s3; -import static org.apache.dolphinscheduler.common.constants.Constants.FOLDER_SEPARATOR; -import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; import org.apache.dolphinscheduler.plugin.storage.api.StorageEntity; import org.apache.dolphinscheduler.spi.enums.ResourceType; -import java.io.IOException; -import java.util.Collections; +import java.nio.file.FileAlreadyExistsException; import java.util.List; +import java.util.stream.Stream; +import lombok.SneakyThrows; + +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; +import org.testcontainers.containers.MinIOContainer; +import org.testcontainers.lifecycle.Startables; +import org.testcontainers.shaded.com.google.common.collect.ImmutableMap; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.ListObjectsV2Request; -import com.amazonaws.services.s3.model.ListObjectsV2Result; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; -@ExtendWith(MockitoExtension.class) public class S3StorageOperatorTest { - private static final String ACCESS_KEY_ID_MOCK = "ACCESS_KEY_ID_MOCK"; + private static final String demoSql = S3StorageOperatorTest.class.getResource("/demo.sql").getFile(); - private static final String ACCESS_KEY_SECRET_MOCK = "ACCESS_KEY_SECRET_MOCK"; + private static MinIOContainer minIOContainer; - private static final String REGION_MOCK = "REGION_MOCK"; + private static S3StorageOperator s3StorageOperator; - private static final String END_POINT_MOCK = "END_POINT_MOCK"; + @BeforeAll + public static void setUp() throws Exception { + String bucketName = "dolphinscheduler"; + String accessKey = "accessKey123"; + String secretKey = "secretKey123"; + String region = "us-east-1"; - private static final String BUCKET_NAME_MOCK = "BUCKET_NAME_MOCK"; + minIOContainer = new MinIOContainer("minio/minio:RELEASE.2023-09-04T19-57-37Z") + .withEnv("MINIO_ACCESS_KEY", accessKey) + .withEnv("MINIO_SECRET_KEY", secretKey) + .withEnv("MINIO_REGION", region) + .withNetworkAliases(bucketName + "." + "localhost"); - private static final String TENANT_CODE_MOCK = "TENANT_CODE_MOCK"; + Startables.deepStart(Stream.of(minIOContainer)).join(); - private static final String DIR_MOCK = "DIR_MOCK"; + String endpoint = minIOContainer.getS3URL(); - private static final String FILE_NAME_MOCK = "FILE_NAME_MOCK"; + AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard() + .withEndpointConfiguration(new AmazonS3ClientBuilder.EndpointConfiguration(endpoint, region)) + .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey))) + .withPathStyleAccessEnabled(true) + .build(); + amazonS3.createBucket(bucketName); - private static final String FILE_PATH_MOCK = "FILE_PATH_MOCK"; - - private static final String FULL_NAME = "/tmp/dir1/"; - - private static final String DEFAULT_PATH = "/tmp/"; - - @Mock - private AmazonS3 s3Client; - - private S3StorageOperator s3StorageOperator; + S3StorageProperties s3StorageProperties = S3StorageProperties.builder() + .bucketName(bucketName) + .resourceUploadPath("tmp/dolphinscheduler") + .s3Configuration(ImmutableMap.of( + "access.key.id", accessKey, + "access.key.secret", secretKey, + "region", region, + "endpoint", endpoint)) + .build(); + s3StorageOperator = new S3StorageOperator(s3StorageProperties); + } @BeforeEach - public void setUp() throws Exception { - s3StorageOperator = Mockito.spy(new S3StorageOperator()); - - doReturn(BUCKET_NAME_MOCK).when(s3StorageOperator).readBucketName(); - Mockito.doReturn(s3Client) - .when(s3StorageOperator).buildS3Client(); - Mockito.doNothing() - .when(s3StorageOperator).checkBucketNameExists(Mockito.any()); - - s3StorageOperator.init(); + public void initializeFiles() { + s3StorageOperator.delete("tmp/dolphinscheduler/default/resources", true); + s3StorageOperator.createStorageDir("tmp/dolphinscheduler/default/resources/sqlDirectory"); + s3StorageOperator.createStorageDir("tmp/dolphinscheduler/default/resources/multipleDirectories"); + s3StorageOperator.createStorageDir("tmp/dolphinscheduler/default/resources/multipleDirectories/1"); + s3StorageOperator.createStorageDir("tmp/dolphinscheduler/default/resources/multipleDirectories/2"); + s3StorageOperator.createStorageDir("tmp/dolphinscheduler/default/resources/multipleDirectories/3"); + s3StorageOperator.upload(demoSql, "tmp/dolphinscheduler/default/resources/multipleDirectories/1/demo.sql", + false, true); + s3StorageOperator.createStorageDir("tmp/dolphinscheduler/default/resources/emptyDirectory"); + s3StorageOperator.upload(demoSql, "tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql", false, true); } @Test - public void testInit() { - verify(s3StorageOperator, times(1)).buildS3Client(); - Assertions.assertEquals(BUCKET_NAME_MOCK, s3StorageOperator.getBucketName()); + public void testGetResourceMetaData() { + ResourceMetadata resourceMetaData = + s3StorageOperator.getResourceMetaData("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql"); + assertEquals("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql", + resourceMetaData.getResourceAbsolutePath()); + assertEquals("tmp/dolphinscheduler", resourceMetaData.getResourceBaseDirectory()); + assertEquals("default", resourceMetaData.getTenant()); + assertEquals(ResourceType.FILE, resourceMetaData.getResourceType()); + assertEquals("sqlDirectory/demo.sql", resourceMetaData.getResourceRelativePath()); + assertEquals("tmp/dolphinscheduler/default/resources/sqlDirectory", + resourceMetaData.getResourceParentAbsolutePath()); + assertFalse(resourceMetaData.isDirectory()); } @Test - public void testTearDown() throws IOException { - doNothing().when(s3Client).shutdown(); - s3StorageOperator.close(); - verify(s3Client, times(1)).shutdown(); + public void testGetStorageBaseDirectory() { + assertEquals("tmp/dolphinscheduler", s3StorageOperator.getStorageBaseDirectory()); } @Test - public void testCreateTenantResAndUdfDir() throws Exception { - doReturn(DIR_MOCK).when(s3StorageOperator).getS3ResDir(TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(s3StorageOperator).getS3UdfDir(TENANT_CODE_MOCK); - doReturn(true).when(s3StorageOperator).mkdir(TENANT_CODE_MOCK, DIR_MOCK); - s3StorageOperator.createTenantDirIfNotExists(TENANT_CODE_MOCK); - verify(s3StorageOperator, times(2)).mkdir(TENANT_CODE_MOCK, DIR_MOCK); + public void testGetStorageBaseDirectory_withTenant() { + assertEquals("tmp/dolphinscheduler/default", s3StorageOperator.getStorageBaseDirectory("default")); } @Test - public void testGetResDir() { - final String expectedResourceDir = String.format("dolphinscheduler/%s/resources/", TENANT_CODE_MOCK); - final String dir = s3StorageOperator.getResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedResourceDir, dir); + public void testGetStorageBaseDirectory_withTenant_withResourceTypeFile() { + String storageBaseDirectory = s3StorageOperator.getStorageBaseDirectory("default", ResourceType.FILE); + assertThat(storageBaseDirectory).isEqualTo("tmp/dolphinscheduler/default/resources"); } @Test - public void testGetUdfDir() { - final String expectedUdfDir = String.format("dolphinscheduler/%s/udfs/", TENANT_CODE_MOCK); - final String dir = s3StorageOperator.getUdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedUdfDir, dir); + public void testGetStorageBaseDirectory_withTenant_withResourceTypeAll() { + String storageBaseDirectory = s3StorageOperator.getStorageBaseDirectory("default", ResourceType.ALL); + assertThat(storageBaseDirectory).isEqualTo("tmp/dolphinscheduler/default"); } @Test - public void mkdirWhenDirExists() { - boolean isSuccess = false; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - doReturn(true).when(s3Client).doesObjectExist(BUCKET_NAME_MOCK, key); - isSuccess = s3StorageOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - verify(s3Client, times(1)).doesObjectExist(BUCKET_NAME_MOCK, key); + public void testGetStorageFileAbsolutePath() { + assertThat(s3StorageOperator.getStorageFileAbsolutePath("default", "demo.sql")) + .isEqualTo("tmp/dolphinscheduler/default/resources/demo.sql"); + } - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); + @Test + public void testCreateStorageDir_notExist() { + String dirName = "tmp/dolphinscheduler/default/resources/testDirectory"; + s3StorageOperator.createStorageDir(dirName); + assertTrue(s3StorageOperator.exists(dirName)); + + } + + @Test + public void testCreateStorageDir_exist() { + final String dirName = "tmp/dolphinscheduler/default/resources/emptyDirectory"; + Assertions.assertThrows(FileAlreadyExistsException.class, () -> s3StorageOperator.createStorageDir(dirName)); + } + + @Test + public void testExists_fileExist() { + assertTrue(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql")); + } + + @Test + public void testExists_fileNotExist() { + assertFalse(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/sqlDirectory/notExist.sql")); + } + + @Test + public void testExists_directoryExist() { + assertTrue(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/sqlDirectory")); + } + + @Test + public void testExists_directoryNotExist() { + assertFalse(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/notExistDirectory")); + } + + @Test + public void delete_fileExist() { + s3StorageOperator.delete("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql", true); + assertFalse(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql")); + } + + @Test + public void delete_fileNotExist() { + s3StorageOperator.delete("tmp/dolphinscheduler/default/resources/sqlDirectory/notExist.sql", true); + assertFalse(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/sqlDirectory/notExist.sql")); + } + + @Test + public void delete_directoryExist() { + s3StorageOperator.delete("tmp/dolphinscheduler/default/resources/sqlDirectory", true); + assertFalse(s3StorageOperator.exists("/tmp/dolphinscheduler/default/resources/sqlDirectory")); + } + + @Test + public void delete_directoryNotExist() { + s3StorageOperator.delete("tmp/dolphinscheduler/default/resources/notExist", true); + assertFalse(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/notExist")); + } + + @Test + public void copy_file() { + s3StorageOperator.copy("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql", + "tmp/dolphinscheduler/default/resources/sqlDirectory/demo_copy.sql", true, true); + assertTrue(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/sqlDirectory/demo_copy.sql")); + assertFalse(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql")); + } + + @Test + public void copy_directory() { + assertThrows(UnsupportedOperationException.class, + () -> s3StorageOperator.copy("tmp/dolphinscheduler/default/resources/sqlDirectory", + "tmp/dolphinscheduler/default/resources/sqlDirectory_copy", true, true)); + } + + @Test + public void testUpload_file() { + String file = S3StorageOperatorTest.class.getResource("/student.sql").getFile(); + s3StorageOperator.upload(file, "tmp/dolphinscheduler/default/resources/sqlDirectory/student.sql", false, true); + assertTrue(s3StorageOperator.exists("tmp/dolphinscheduler/default/resources/sqlDirectory/student.sql")); + } + + @Test + public void testFetchFileContent() { + List strings = s3StorageOperator + .fetchFileContent("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql", 0, 2); + assertThat(strings).hasSize(2); + } + + @Test + public void testListStorageEntity_file() { + List storageEntities = + s3StorageOperator.listStorageEntity("tmp/dolphinscheduler/default/resources/sqlDirectory"); + assertThat(storageEntities).hasSize(1); + + StorageEntity storageEntity = storageEntities.get(0); + assertThat(storageEntity.getFullName()) + .isEqualTo("tmp/dolphinscheduler/default/resources/sqlDirectory/demo.sql"); + assertThat(storageEntity.getFileName()) + .isEqualTo("demo.sql"); + assertThat(storageEntity.isDirectory()).isFalse(); + assertThat(storageEntity.getPfullName()).isEqualTo("tmp/dolphinscheduler/default/resources/sqlDirectory"); + assertThat(storageEntity.getType()).isEqualTo(ResourceType.FILE); + } + + @Test + public void testListStorageEntity_directory() { + List storageEntities = + s3StorageOperator.listStorageEntity("tmp/dolphinscheduler/default/resources"); + assertThat(storageEntities).hasSize(3); + + } + + @Test + public void testListStorageEntity_directoryNotExist() { + List storageEntities = + s3StorageOperator.listStorageEntity("tmp/dolphinscheduler/notExist/resources"); + assertThat(storageEntities).isEmpty(); + + } + + @Test + public void testListStorageEntityRecursively() { + List storageEntities = + s3StorageOperator + .listFileStorageEntityRecursively("tmp/dolphinscheduler/default/resources/multipleDirectories"); + assertThat(storageEntities).hasSize(1); + + StorageEntity storageEntity = storageEntities.get(0); + assertThat(storageEntity.getFullName()) + .isEqualTo("tmp/dolphinscheduler/default/resources/multipleDirectories/1/demo.sql"); + assertThat(storageEntity.getFileName()) + .isEqualTo("demo.sql"); + assertThat(storageEntity.isDirectory()).isFalse(); + assertThat(storageEntity.getPfullName()) + .isEqualTo("tmp/dolphinscheduler/default/resources/multipleDirectories/1"); + assertThat(storageEntity.getType()).isEqualTo(ResourceType.FILE); + + } + + @Test + public void testExceptionWhenBucketNameNotExists() { + Assertions.assertDoesNotThrow(() -> s3StorageOperator.exceptionWhenBucketNameNotExists("dolphinscheduler")); + } + + @SneakyThrows + @AfterAll + public static void tearDown() { + if (s3StorageOperator != null) { + s3StorageOperator.close(); } - - Assertions.assertTrue(isSuccess); - } - - @Test - public void mkdirWhenDirNotExists() { - boolean isSuccess = true; - try { - final String key = DIR_MOCK + FOLDER_SEPARATOR; - doReturn(false).when(s3Client).doesObjectExist(BUCKET_NAME_MOCK, key); - isSuccess = s3StorageOperator.mkdir(TENANT_CODE_MOCK, DIR_MOCK); - verify(s3Client, times(1)).doesObjectExist(BUCKET_NAME_MOCK, key); - - } catch (IOException e) { - Assertions.fail("test failed due to unexpected IO exception"); + if (minIOContainer != null) { + minIOContainer.stop(); } - - Assertions.assertTrue(isSuccess); } - @Test - public void getResourceFullName() { - final String expectedResourceFullName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFullName = s3StorageOperator.getResourceFullName(TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedResourceFullName, resourceFullName); - } - - @Test - public void getResourceFileName() { - final String expectedResourceFileName = FILE_NAME_MOCK; - final String resourceFullName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String resourceFileName = s3StorageOperator.getResourceFileName(TENANT_CODE_MOCK, resourceFullName); - Assertions.assertEquals(expectedResourceFileName, resourceFileName); - } - - @Test - public void getFileName() { - final String expectedFileName = - String.format("dolphinscheduler/%s/resources/%s", TENANT_CODE_MOCK, FILE_NAME_MOCK); - final String fileName = s3StorageOperator.getFileName(ResourceType.FILE, TENANT_CODE_MOCK, FILE_NAME_MOCK); - Assertions.assertEquals(expectedFileName, fileName); - } - - @Test - public void exists() { - boolean doesExist = false; - doReturn(true).when(s3Client).doesObjectExist(BUCKET_NAME_MOCK, FILE_NAME_MOCK); - try { - doesExist = s3StorageOperator.exists(FILE_NAME_MOCK); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(doesExist); - verify(s3Client, times(1)).doesObjectExist(BUCKET_NAME_MOCK, FILE_NAME_MOCK); - } - - @Test - public void delete() { - doNothing().when(s3Client).deleteObject(anyString(), anyString()); - try { - s3StorageOperator.delete(FILE_NAME_MOCK, true); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - verify(s3Client, times(1)).deleteObject(anyString(), anyString()); - } - - @Test - public void copy() { - boolean isSuccess = false; - doReturn(null).when(s3Client).copyObject(anyString(), anyString(), anyString(), anyString()); - try { - isSuccess = s3StorageOperator.copy(FILE_PATH_MOCK, FILE_PATH_MOCK, false, false); - } catch (IOException e) { - Assertions.fail("unexpected IO exception in unit test"); - } - - Assertions.assertTrue(isSuccess); - verify(s3Client, times(1)).copyObject(anyString(), anyString(), anyString(), anyString()); - } - - @Test - public void deleteTenant() { - doNothing().when(s3StorageOperator).deleteTenantCode(anyString()); - try { - s3StorageOperator.deleteTenant(TENANT_CODE_MOCK); - } catch (Exception e) { - Assertions.fail("unexpected exception caught in unit test"); - } - - verify(s3StorageOperator, times(1)).deleteTenantCode(anyString()); - } - - @Test - public void testGetS3ResDir() { - final String expectedS3ResDir = String.format("dolphinscheduler/%s/resources", TENANT_CODE_MOCK); - final String s3ResDir = s3StorageOperator.getS3ResDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedS3ResDir, s3ResDir); - } - - @Test - public void testGetS3UdfDir() { - final String expectedS3UdfDir = String.format("dolphinscheduler/%s/udfs", TENANT_CODE_MOCK); - final String s3UdfDir = s3StorageOperator.getS3UdfDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedS3UdfDir, s3UdfDir); - } - - @Test - public void testGetS3TenantDir() { - final String expectedS3TenantDir = String.format(FORMAT_S_S, DIR_MOCK, TENANT_CODE_MOCK); - doReturn(DIR_MOCK).when(s3StorageOperator).getS3DataBasePath(); - final String s3TenantDir = s3StorageOperator.getS3TenantDir(TENANT_CODE_MOCK); - Assertions.assertEquals(expectedS3TenantDir, s3TenantDir); - } - - @Test - public void deleteDir() { - doReturn(true).when(s3Client).doesObjectExist(anyString(), anyString()); - s3StorageOperator.deleteDir(DIR_MOCK); - verify(s3Client, times(1)).deleteObject(anyString(), anyString()); - } - - @Test - public void testGetFileStatus() throws Exception { - doReturn(new ListObjectsV2Result()).when(s3Client).listObjectsV2(Mockito.any(ListObjectsV2Request.class)); - StorageEntity entity = - s3StorageOperator.getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(FULL_NAME, entity.getFullName()); - Assertions.assertEquals("dir1/", entity.getFileName()); - } - - @Test - public void testListFilesStatus() throws Exception { - doReturn(new ListObjectsV2Result()).when(s3Client).listObjectsV2(Mockito.any(ListObjectsV2Request.class)); - List result = - s3StorageOperator.listFilesStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, ResourceType.FILE); - Assertions.assertEquals(0, result.size()); - } - - @Test - public void testListFilesStatusRecursively() throws Exception { - StorageEntity entity = new StorageEntity(); - entity.setFullName(FULL_NAME); - - doReturn(entity).when(s3StorageOperator).getFileStatus(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, - ResourceType.FILE); - doReturn(Collections.EMPTY_LIST).when(s3StorageOperator).listFilesStatus(anyString(), anyString(), anyString(), - Mockito.any(ResourceType.class)); - - List result = - s3StorageOperator.listFilesStatusRecursively(FULL_NAME, DEFAULT_PATH, TENANT_CODE_MOCK, - ResourceType.FILE); - Assertions.assertEquals(0, result.size()); - } } diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/demo.sql b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/demo.sql new file mode 100644 index 0000000000..2c3a4cd616 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/demo.sql @@ -0,0 +1,17 @@ +/* + * 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. +*/ +select * from t_ds_version; \ No newline at end of file diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/student.sql b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/student.sql new file mode 100644 index 0000000000..d91aaf0803 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/student.sql @@ -0,0 +1,17 @@ +/* + * 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. +*/ +select * from t_ds_student; \ No newline at end of file diff --git a/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/student/student.sql b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/student/student.sql new file mode 100644 index 0000000000..6eab26f880 --- /dev/null +++ b/dolphinscheduler-storage-plugin/dolphinscheduler-storage-s3/src/test/resources/student/student.sql @@ -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. +*/ + +select * from t_ds_student; \ No newline at end of file diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/pom.xml b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/pom.xml index b115a1b6d1..7c8f24c717 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/pom.xml +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/pom.xml @@ -297,5 +297,10 @@ org.projectlombok lombok + + org.junit.jupiter + junit-jupiter + test + diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/ILogicTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/ILogicTaskChannel.java new file mode 100644 index 0000000000..7b9766f57d --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/ILogicTaskChannel.java @@ -0,0 +1,24 @@ +/* + * 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.dolphinscheduler.plugin.task.api; + +/** + * Used to mark a task channel as a logic task channel, the logic task channel is a special task channel that will be executed at master. + */ +public interface ILogicTaskChannel extends TaskChannel { +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskChannel.java index 77abae5047..89fa2a07da 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskChannel.java @@ -18,19 +18,11 @@ package org.apache.dolphinscheduler.plugin.task.api; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public interface TaskChannel { - void cancelApplication(boolean status); - - // todo: return ITask AbstractTask createTask(TaskExecutionContext taskRequest); - // todo: return IParameters - AbstractParameters parseParameters(ParametersNode parametersNode); - - ResourceParametersHelper getResources(String parameters); + AbstractParameters parseParameters(String taskParams); } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskChannelFactory.java index f7719b4728..7176f1e82f 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskChannelFactory.java @@ -17,16 +17,19 @@ package org.apache.dolphinscheduler.plugin.task.api; -import org.apache.dolphinscheduler.spi.common.UiChannelFactory; import org.apache.dolphinscheduler.spi.plugin.PrioritySPI; import org.apache.dolphinscheduler.spi.plugin.SPIIdentify; -public interface TaskChannelFactory extends UiChannelFactory, PrioritySPI { +public interface TaskChannelFactory extends PrioritySPI { + + default SPIIdentify getIdentify() { + return SPIIdentify.builder() + .name(getName()) + .build(); + } + + String getName(); TaskChannel create(); - default SPIIdentify getIdentify() { - return SPIIdentify.builder().name(getName()).build(); - } - } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskConstants.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskConstants.java index 43734416e7..98780a2236 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskConstants.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskConstants.java @@ -362,24 +362,10 @@ public class TaskConstants { */ public static final String DATA_QUALITY_JAR_DIR = "data-quality.jar.dir"; - public static final String TASK_TYPE_CONDITIONS = "CONDITIONS"; - - public static final String TASK_TYPE_SWITCH = "SWITCH"; - - public static final String TASK_TYPE_SUB_PROCESS = "SUB_PROCESS"; - - public static final String TASK_TYPE_DYNAMIC = "DYNAMIC"; - - public static final String TASK_TYPE_DEPENDENT = "DEPENDENT"; - - public static final String TASK_TYPE_SQL = "SQL"; - public static final String TASK_TYPE_DATA_QUALITY = "DATA_QUALITY"; public static final Set TASK_TYPE_SET_K8S = Sets.newHashSet("K8S", "KUBEFLOW"); - public static final String TASK_TYPE_BLOCKING = "BLOCKING"; - /** * azure config */ @@ -389,13 +375,6 @@ public class TaskConstants { public static final String AZURE_SECRET_TENANT_ID = "resource.azure.tenant.id"; public static final String QUERY_INTERVAL = "resource.query.interval"; - /** - * aws config - */ - public static final String AWS_ACCESS_KEY_ID = "resource.aws.access.key.id"; - public static final String AWS_SECRET_ACCESS_KEY = "resource.aws.secret.access.key"; - public static final String AWS_REGION = "resource.aws.region"; - /** * alibaba cloud config */ diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskPluginManager.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskPluginManager.java index 938aa77459..7e61fe4dff 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskPluginManager.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/TaskPluginManager.java @@ -17,21 +17,13 @@ package org.apache.dolphinscheduler.plugin.task.api; -import org.apache.dolphinscheduler.common.utils.JSONUtils; +import static com.google.common.base.Preconditions.checkNotNull; + import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.BlockingParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ConditionsParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.DynamicParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.SubProcessParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.SwitchParameters; import org.apache.dolphinscheduler.spi.plugin.PrioritySPIFactory; -import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import lombok.extern.slf4j.Slf4j; @@ -39,15 +31,15 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class TaskPluginManager { - private static final Map taskChannelFactoryMap = new HashMap<>(); private static final Map taskChannelMap = new HashMap<>(); private static final AtomicBoolean loadedFlag = new AtomicBoolean(false); - /** - * Load task plugins from classpath. - */ - public static void loadPlugin() { + static { + loadTaskPlugin(); + } + + public static void loadTaskPlugin() { if (!loadedFlag.compareAndSet(false, true)) { log.warn("The task plugin has already been loaded"); return; @@ -59,7 +51,6 @@ public class TaskPluginManager { log.info("Registering task plugin: {} - {}", factoryName, factory.getClass().getSimpleName()); - taskChannelFactoryMap.put(factoryName, factory); taskChannelMap.put(factoryName, factory.create()); log.info("Registered task plugin: {} - {}", factoryName, factory.getClass().getSimpleName()); @@ -67,48 +58,49 @@ public class TaskPluginManager { } - public static Map getTaskChannelMap() { - return Collections.unmodifiableMap(taskChannelMap); - } - - public static Map getTaskChannelFactoryMap() { - return Collections.unmodifiableMap(taskChannelFactoryMap); - } - + /** + * Get the TaskChannel by type, if the TaskChannel is not found, will throw + * @param type task type, cannot be null + * @throws IllegalArgumentException if the TaskChannel is not found + */ public static TaskChannel getTaskChannel(String type) { - return getTaskChannelMap().get(type); + checkNotNull(type, "type cannot be null"); + TaskChannel taskChannel = taskChannelMap.get(type); + if (taskChannel == null) { + throw new IllegalArgumentException("Cannot find TaskChannel for : " + type); + } + return taskChannel; } - public static boolean checkTaskParameters(ParametersNode parametersNode) { - AbstractParameters abstractParameters = getParameters(parametersNode); - return abstractParameters != null && abstractParameters.checkParameters(); + /** + * Check if the task parameters is validated + * @param taskType task type, cannot be null + * @param taskParams task parameters + * @return true if the task parameters is validated, otherwise false + * @throws IllegalArgumentException if the TaskChannel is not found + * @throws IllegalArgumentException if cannot deserialize the task parameters + */ + public static boolean checkTaskParameters(String taskType, String taskParams) { + AbstractParameters abstractParameters = parseTaskParameters(taskType, taskParams); + return abstractParameters.checkParameters(); } - public static AbstractParameters getParameters(ParametersNode parametersNode) { - String taskType = parametersNode.getTaskType(); - if (Objects.isNull(taskType)) { - return null; - } - switch (taskType) { - case TaskConstants.TASK_TYPE_CONDITIONS: - return JSONUtils.parseObject(parametersNode.getTaskParams(), ConditionsParameters.class); - case TaskConstants.TASK_TYPE_SWITCH: - return JSONUtils.parseObject(parametersNode.getTaskParams(), SwitchParameters.class); - case TaskConstants.TASK_TYPE_SUB_PROCESS: - return JSONUtils.parseObject(parametersNode.getTaskParams(), SubProcessParameters.class); - case TaskConstants.TASK_TYPE_DEPENDENT: - return JSONUtils.parseObject(parametersNode.getTaskParams(), DependentParameters.class); - case TaskConstants.TASK_TYPE_BLOCKING: - return JSONUtils.parseObject(parametersNode.getTaskParams(), BlockingParameters.class); - case TaskConstants.TASK_TYPE_DYNAMIC: - return JSONUtils.parseObject(parametersNode.getTaskParams(), DynamicParameters.class); - default: - TaskChannel taskChannel = getTaskChannelMap().get(taskType); - if (Objects.isNull(taskChannel)) { - return null; - } - return taskChannel.parseParameters(parametersNode); + /** + * Parse the task parameters + * @param taskType task type, cannot be null + * @param taskParams task parameters + * @return AbstractParameters + * @throws IllegalArgumentException if the TaskChannel is not found + * @throws IllegalArgumentException if cannot deserialize the task parameters + */ + public static AbstractParameters parseTaskParameters(String taskType, String taskParams) { + checkNotNull(taskType, "taskType cannot be null"); + TaskChannel taskChannel = getTaskChannel(taskType); + AbstractParameters abstractParameters = taskChannel.parseParameters(taskParams); + if (abstractParameters == null) { + throw new IllegalArgumentException("Cannot parse task parameters: " + taskParams + " for : " + taskType); } + return abstractParameters; } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/log/TaskInstanceLogHeader.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/log/TaskInstanceLogHeader.java index 02ee2e1c99..3aa76dd114 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/log/TaskInstanceLogHeader.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/log/TaskInstanceLogHeader.java @@ -27,11 +27,11 @@ import com.google.common.collect.Lists; public class TaskInstanceLogHeader { private static final List INITIALIZE_TASK_CONTEXT_HEADER = Lists.newArrayList( - "***********************************************************************************************", + "\n***********************************************************************************************", "********************************* Initialize task context ***********************************", "***********************************************************************************************"); private static final List LOAD_TASK_INSTANCE_PLUGIN_HEADER = Lists.newArrayList( - "***********************************************************************************************", + "\n***********************************************************************************************", "********************************* Load task instance plugin *********************************", "***********************************************************************************************"); @@ -40,12 +40,12 @@ public class TaskInstanceLogHeader { } private static final List EXECUTE_TASK_HEADER = Lists.newArrayList( - "***********************************************************************************************", + "\n***********************************************************************************************", "********************************* Execute task instance *************************************", "***********************************************************************************************"); private static final List FINALIZE_TASK_HEADER = Lists.newArrayList( - "***********************************************************************************************", + "\n***********************************************************************************************", "********************************* Finalize task instance ************************************", "***********************************************************************************************"); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/DynamicInputParameter.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/DynamicInputParameter.java index 40f85087cf..cd5c1005c4 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/DynamicInputParameter.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/DynamicInputParameter.java @@ -17,12 +17,16 @@ package org.apache.dolphinscheduler.plugin.task.api.model; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.NonNull; @Data +@Builder @NoArgsConstructor +@AllArgsConstructor public class DynamicInputParameter { @NonNull diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/SwitchResultVo.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/SwitchResultVo.java index 3af8fad195..51d53080eb 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/SwitchResultVo.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/model/SwitchResultVo.java @@ -17,9 +17,6 @@ package org.apache.dolphinscheduler.plugin.task.api.model; -import java.util.ArrayList; -import java.util.List; - import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -30,15 +27,5 @@ import lombok.NoArgsConstructor; public class SwitchResultVo { private String condition; - private List nextNode; - - public void setNextNode(Object nextNode) { - if (nextNode instanceof Long) { - List nextNodeList = new ArrayList<>(); - nextNodeList.add((Long) nextNode); - this.nextNode = nextNodeList; - } else { - this.nextNode = (ArrayList) nextNode; - } - } + private Long nextNode; } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/ConditionsParameters.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/ConditionsParameters.java index 15141937b0..8ad567578c 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/ConditionsParameters.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/ConditionsParameters.java @@ -19,41 +19,60 @@ package org.apache.dolphinscheduler.plugin.task.api.parameters; import org.apache.dolphinscheduler.plugin.task.api.enums.DependentRelation; import org.apache.dolphinscheduler.plugin.task.api.model.DependentTaskModel; -import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo; -import java.util.ArrayList; +import org.apache.commons.collections4.CollectionUtils; + import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@Builder +@NoArgsConstructor +@AllArgsConstructor public class ConditionsParameters extends AbstractParameters { - // depend node list and state, only need task name - private List dependTaskList; - private DependentRelation dependRelation; + private ConditionDependency dependence; - // node list to run when success - private List successNode; - - // node list to run when failed - private List failedNode; + private ConditionResult conditionResult; @Override public boolean checkParameters() { + if (dependence == null || CollectionUtils.isEmpty(dependence.getDependTaskList())) { + return false; + } + if (conditionResult == null || CollectionUtils.isEmpty(conditionResult.getSuccessNode()) + || CollectionUtils.isEmpty(conditionResult.getFailedNode())) { + return false; + } return true; } - @Override - public List getResourceFilesList() { - return new ArrayList<>(); + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ConditionDependency { + + private List dependTaskList; + + private DependentRelation relation; } - public String getConditionResult() { - return "{" - + "\"successNode\": [\"" + successNode.get(0) - + "\"],\"failedNode\": [\"" + failedNode.get(0) - + "\"]}"; + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ConditionResult { + + private boolean conditionSuccess; + + private List successNode; + + private List failedNode; } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/DependentParameters.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/DependentParameters.java index 1e648d5775..c5f9ed916f 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/DependentParameters.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/DependentParameters.java @@ -20,28 +20,55 @@ package org.apache.dolphinscheduler.plugin.task.api.parameters; import org.apache.dolphinscheduler.plugin.task.api.enums.DependentRelation; import org.apache.dolphinscheduler.plugin.task.api.model.DependentTaskModel; +import org.apache.commons.collections4.CollectionUtils; + import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; @Data @EqualsAndHashCode(callSuper = true) public class DependentParameters extends AbstractParameters { - private List dependTaskList; - private DependentRelation relation; - /** Time unit is second */ - private Integer checkInterval; - private DependentFailurePolicyEnum failurePolicy; - /** Time unit is minutes */ - private Integer failureWaitingTime; + private Dependence dependence; @Override public boolean checkParameters() { + if (dependence == null) { + return false; + } + if (CollectionUtils.isEmpty(dependence.getDependTaskList())) { + return false; + } + if (dependence.getCheckInterval() != null && dependence.getCheckInterval() <= 0) { + return false; + } return true; } + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Dependence { + + private List dependTaskList; + private DependentRelation relation; + /** + * Time unit is second + */ + private Integer checkInterval; + private DependentFailurePolicyEnum failurePolicy; + /** + * Time unit is minutes + */ + private Integer failureWaitingTime; + } + /** * the dependent task failure policy. */ diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/DynamicParameters.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/DynamicParameters.java index e918db3b18..2a594655f9 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/DynamicParameters.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/DynamicParameters.java @@ -21,9 +21,15 @@ import org.apache.dolphinscheduler.plugin.task.api.model.DynamicInputParameter; import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@Builder +@NoArgsConstructor +@AllArgsConstructor public class DynamicParameters extends AbstractParameters { /** diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/ParametersNode.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/ParametersNode.java deleted file mode 100644 index b6eb88a68a..0000000000 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/ParametersNode.java +++ /dev/null @@ -1,116 +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.dolphinscheduler.plugin.task.api.parameters; - -/** - * TODO

Need to optimize, why there are multiple task parameter variables:taskParams,dependence,switchResult

- * - */ -public class ParametersNode { - - private String taskType; - - private String taskParams; - - private String dependence; - - private String switchResult; - - public static ParametersNode.ParametersNodeBuilder builder() { - return new ParametersNode.ParametersNodeBuilder(); - } - - public static class ParametersNodeBuilder { - - private String taskType; - - private String taskParams; - - private String dependence; - - private String switchResult; - - public ParametersNodeBuilder taskType(String taskType) { - this.taskType = taskType; - return this; - } - - public ParametersNodeBuilder taskParams(String taskParams) { - this.taskParams = taskParams; - return this; - } - - public ParametersNodeBuilder dependence(String dependence) { - this.dependence = dependence; - return this; - } - - public ParametersNodeBuilder switchResult(String switchResult) { - this.switchResult = switchResult; - return this; - } - - public ParametersNode build() { - return new ParametersNode(this.taskType, this.taskParams, this.dependence, this.switchResult); - } - - } - - public ParametersNode() { - - } - - public ParametersNode(String taskType, String taskParams, String dependence, String switchResult) { - this.taskType = taskType; - this.taskParams = taskParams; - this.dependence = dependence; - this.switchResult = switchResult; - } - - public String getTaskType() { - return taskType; - } - - public void setTaskType(String taskType) { - this.taskType = taskType; - } - - public String getTaskParams() { - return taskParams; - } - - public void setTaskParams(String taskParams) { - this.taskParams = taskParams; - } - - public String getDependence() { - return dependence; - } - - public void setDependence(String dependence) { - this.dependence = dependence; - } - - public String getSwitchResult() { - return switchResult; - } - - public void setSwitchResult(String switchResult) { - this.switchResult = switchResult; - } -} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/SubProcessParameters.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/SubProcessParameters.java index 3368313935..655ba7540c 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/SubProcessParameters.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/SubProcessParameters.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.api.parameters; public class SubProcessParameters extends AbstractParameters { - /** - * process definition id - */ private long processDefinitionCode; public void setProcessDefinitionCode(long processDefinitionCode) { @@ -34,7 +31,7 @@ public class SubProcessParameters extends AbstractParameters { @Override public boolean checkParameters() { - return this.processDefinitionCode != 0; + return this.processDefinitionCode > 0; } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/SwitchParameters.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/SwitchParameters.java index a1928bbe16..655a86f9e0 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/SwitchParameters.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/parameters/SwitchParameters.java @@ -17,69 +17,56 @@ package org.apache.dolphinscheduler.plugin.task.api.parameters; -import org.apache.dolphinscheduler.plugin.task.api.enums.DependentRelation; import org.apache.dolphinscheduler.plugin.task.api.model.SwitchResultVo; -import java.util.ArrayList; +import org.apache.commons.collections4.CollectionUtils; + import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor public class SwitchParameters extends AbstractParameters { - private DependentRelation dependRelation; - private String relation; - private List nextNode; + // due to history reasons, the field name is switchResult + private SwitchResult switchResult; + + // The next branch which should be executed after the switch logic task executed. + private Long nextBranch; @Override public boolean checkParameters() { + if (switchResult == null) { + return false; + } + if (CollectionUtils.isEmpty(switchResult.getDependTaskList()) && switchResult.getNextNode() == null) { + return false; + } + for (SwitchResultVo switchResultVo : switchResult.getDependTaskList()) { + if (switchResultVo == null || switchResultVo.getNextNode() == null) { + return false; + } + } return true; } - private int resultConditionLocation; - private List dependTaskList; + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class SwitchResult { - public DependentRelation getDependRelation() { - return dependRelation; + // switch condition + private List dependTaskList; + + // default branch node code in switch task + private Long nextNode; } - public void setDependRelation(DependentRelation dependRelation) { - this.dependRelation = dependRelation; - } - - public int getResultConditionLocation() { - return resultConditionLocation; - } - - public void setResultConditionLocation(int resultConditionLocation) { - this.resultConditionLocation = resultConditionLocation; - } - - public String getRelation() { - return relation; - } - - public void setRelation(String relation) { - this.relation = relation; - } - - public List getDependTaskList() { - return dependTaskList; - } - - public void setDependTaskList(List dependTaskList) { - this.dependTaskList = dependTaskList; - } - - public List getNextNode() { - return nextNode; - } - - public void setNextNode(Object nextNode) { - if (nextNode instanceof Long) { - List nextNodeList = new ArrayList<>(); - nextNodeList.add((Long) nextNode); - this.nextNode = nextNodeList; - } else { - this.nextNode = (ArrayList) nextNode; - } - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/stream/StreamTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/stream/StreamTaskChannel.java index 6c35b3cec4..bc0107a11e 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/stream/StreamTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/stream/StreamTaskChannel.java @@ -17,13 +17,8 @@ package org.apache.dolphinscheduler.plugin.task.api.stream; -import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; -import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; public interface StreamTaskChannel extends TaskChannel { - AbstractTask pauseTask(TaskExecutionContext taskExecutionContext); - - AbstractTask recoverTask(TaskExecutionContext taskExecutionContext); } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/AbstractLogicTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/AbstractLogicTaskChannel.java new file mode 100644 index 0000000000..b0bcdc8767 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/AbstractLogicTaskChannel.java @@ -0,0 +1,31 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; +import org.apache.dolphinscheduler.plugin.task.api.ILogicTaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; + +public abstract class AbstractLogicTaskChannel implements ILogicTaskChannel { + + @Override + public AbstractTask createTask(TaskExecutionContext taskRequest) { + throw new UnsupportedOperationException("createTask is not supported"); + } + +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/ConditionsLogicTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/ConditionsLogicTaskChannel.java new file mode 100644 index 0000000000..976c658303 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/ConditionsLogicTaskChannel.java @@ -0,0 +1,31 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.plugin.task.api.parameters.ConditionsParameters; + +public class ConditionsLogicTaskChannel extends AbstractLogicTaskChannel { + + @Override + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, ConditionsParameters.class); + } + +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/ConditionsLogicTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/ConditionsLogicTaskChannelFactory.java new file mode 100644 index 0000000000..fd63ea5039 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/ConditionsLogicTaskChannelFactory.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; + +import com.google.auto.service.AutoService; + +@AutoService(TaskChannelFactory.class) +public class ConditionsLogicTaskChannelFactory implements TaskChannelFactory { + + public static final String NAME = "CONDITIONS"; + + @Override + public String getName() { + return NAME; + } + + @Override + public TaskChannel create() { + return new ConditionsLogicTaskChannel(); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DependentLogicTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DependentLogicTaskChannel.java new file mode 100644 index 0000000000..04045f0f4b --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DependentLogicTaskChannel.java @@ -0,0 +1,30 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.plugin.task.api.parameters.DependentParameters; + +public class DependentLogicTaskChannel extends AbstractLogicTaskChannel { + + @Override + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DependentParameters.class); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DependentLogicTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DependentLogicTaskChannelFactory.java new file mode 100644 index 0000000000..332a1c6c42 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DependentLogicTaskChannelFactory.java @@ -0,0 +1,38 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; + +import com.google.auto.service.AutoService; + +@AutoService(TaskChannelFactory.class) +public class DependentLogicTaskChannelFactory implements TaskChannelFactory { + + public static final String NAME = "DEPENDENT"; + @Override + public String getName() { + return NAME; + } + + @Override + public TaskChannel create() { + return new DependentLogicTaskChannel(); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DynamicLogicTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DynamicLogicTaskChannel.java new file mode 100644 index 0000000000..d9f066d773 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DynamicLogicTaskChannel.java @@ -0,0 +1,30 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.plugin.task.api.parameters.DynamicParameters; + +public class DynamicLogicTaskChannel extends AbstractLogicTaskChannel { + + @Override + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DynamicParameters.class); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DynamicLogicTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DynamicLogicTaskChannelFactory.java new file mode 100644 index 0000000000..0164e3ba53 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/DynamicLogicTaskChannelFactory.java @@ -0,0 +1,38 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; + +import com.google.auto.service.AutoService; + +@AutoService(TaskChannelFactory.class) +public class DynamicLogicTaskChannelFactory implements TaskChannelFactory { + + public static final String NAME = "DYNAMIC"; + @Override + public String getName() { + return NAME; + } + + @Override + public TaskChannel create() { + return new DynamicLogicTaskChannel(); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SubWorkflowLogicTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SubWorkflowLogicTaskChannel.java new file mode 100644 index 0000000000..354be54986 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SubWorkflowLogicTaskChannel.java @@ -0,0 +1,30 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.plugin.task.api.parameters.SubProcessParameters; + +public class SubWorkflowLogicTaskChannel extends AbstractLogicTaskChannel { + + @Override + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, SubProcessParameters.class); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SubWorkflowLogicTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SubWorkflowLogicTaskChannelFactory.java new file mode 100644 index 0000000000..06bb6c5ebe --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SubWorkflowLogicTaskChannelFactory.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; + +import com.google.auto.service.AutoService; + +@AutoService(TaskChannelFactory.class) +public class SubWorkflowLogicTaskChannelFactory implements TaskChannelFactory { + + public static final String NAME = "SUB_PROCESS"; + + @Override + public String getName() { + return NAME; + } + + @Override + public TaskChannel create() { + return new SubWorkflowLogicTaskChannel(); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SwitchLogicTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SwitchLogicTaskChannel.java new file mode 100644 index 0000000000..6d549bcb9b --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SwitchLogicTaskChannel.java @@ -0,0 +1,30 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.common.utils.JSONUtils; +import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; +import org.apache.dolphinscheduler.plugin.task.api.parameters.SwitchParameters; + +public class SwitchLogicTaskChannel extends AbstractLogicTaskChannel { + + @Override + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, SwitchParameters.class); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SwitchLogicTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SwitchLogicTaskChannelFactory.java new file mode 100644 index 0000000000..3f5b254ca6 --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/task/SwitchLogicTaskChannelFactory.java @@ -0,0 +1,39 @@ +/* + * 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.dolphinscheduler.plugin.task.api.task; + +import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; + +import com.google.auto.service.AutoService; + +@AutoService(TaskChannelFactory.class) +public class SwitchLogicTaskChannelFactory implements TaskChannelFactory { + + public static final String NAME = "SWITCH"; + + @Override + public String getName() { + return NAME; + } + + @Override + public TaskChannel create() { + return new SwitchLogicTaskChannel(); + } +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/TaskTypeUtils.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/TaskTypeUtils.java new file mode 100644 index 0000000000..cf1f2022ea --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/TaskTypeUtils.java @@ -0,0 +1,55 @@ +/* + * 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.dolphinscheduler.plugin.task.api.utils; + +import org.apache.dolphinscheduler.plugin.task.api.ILogicTaskChannel; +import org.apache.dolphinscheduler.plugin.task.api.TaskPluginManager; +import org.apache.dolphinscheduler.plugin.task.api.task.ConditionsLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.DynamicLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.SubWorkflowLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.SwitchLogicTaskChannelFactory; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class TaskTypeUtils { + + public boolean isSwitchTask(String taskType) { + return SwitchLogicTaskChannelFactory.NAME.equals(taskType); + } + + public boolean isConditionTask(String taskType) { + return ConditionsLogicTaskChannelFactory.NAME.equals(taskType); + } + + public boolean isSubWorkflowTask(String taskType) { + return SubWorkflowLogicTaskChannelFactory.NAME.equals(taskType); + } + + public boolean isDependentTask(String taskType) { + return SubWorkflowLogicTaskChannelFactory.NAME.equals(taskType); + } + + public boolean isDynamicTask(String taskType) { + return DynamicLogicTaskChannelFactory.NAME.equals(taskType); + } + + public boolean isLogicTask(String taskType) { + return TaskPluginManager.getTaskChannel(taskType) instanceof ILogicTaskChannel; + } + +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/TaskUtils.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/TaskUtils.java deleted file mode 100644 index 606058d40f..0000000000 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/TaskUtils.java +++ /dev/null @@ -1,50 +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.dolphinscheduler.plugin.task.api.utils; - -import java.util.Set; - -import lombok.experimental.UtilityClass; - -import com.google.common.collect.Sets; - -@UtilityClass -public class TaskUtils { - - private final String blockingLogicTask = "BLOCKING"; - private final String conditionLogicTask = "CONDITIONS"; - - private final String dependentLogicTask = "DEPENDENT"; - private final String subWorkflowLogicTask = "SUB_PROCESS"; - private final String switchLogicTask = "SWITCH"; - private final String dynamicLogicTask = "DYNAMIC"; - - // todo: Add to SPI - private final Set MASTER_TASK_TYPES = Sets.newHashSet( - blockingLogicTask, - conditionLogicTask, - dependentLogicTask, - subWorkflowLogicTask, - switchLogicTask, - dynamicLogicTask); - - // todo: add to task plugin spi - public boolean isLogicTask(String taskType) { - return MASTER_TASK_TYPES.contains(taskType); - } - -} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/java/org/apache/dolphinscheduler/plugin/task/api/TaskPluginManagerTest.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/java/org/apache/dolphinscheduler/plugin/task/api/TaskPluginManagerTest.java new file mode 100644 index 0000000000..a380af488f --- /dev/null +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/java/org/apache/dolphinscheduler/plugin/task/api/TaskPluginManagerTest.java @@ -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. + */ + +package org.apache.dolphinscheduler.plugin.task.api; + +import static com.google.common.truth.Truth.assertThat; + +import org.apache.dolphinscheduler.plugin.task.api.task.ConditionsLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.DependentLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.DynamicLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.SubWorkflowLogicTaskChannelFactory; +import org.apache.dolphinscheduler.plugin.task.api.task.SwitchLogicTaskChannelFactory; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class TaskPluginManagerTest { + + @ParameterizedTest + @ValueSource(strings = { + ConditionsLogicTaskChannelFactory.NAME, + DependentLogicTaskChannelFactory.NAME, + DynamicLogicTaskChannelFactory.NAME, + SubWorkflowLogicTaskChannelFactory.NAME, + SwitchLogicTaskChannelFactory.NAME}) + void testGetTaskChannel_logicTaskChannel(String type) { + assertThat(TaskPluginManager.getTaskChannel(type)).isNotNull(); + } + +} diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-chunjun/src/main/java/org/apache/dolphinscheduler/plugin/task/chunjun/ChunJunTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-chunjun/src/main/java/org/apache/dolphinscheduler/plugin/task/chunjun/ChunJunTaskChannel.java index 02063d07c1..cce2e28090 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-chunjun/src/main/java/org/apache/dolphinscheduler/plugin/task/chunjun/ChunJunTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-chunjun/src/main/java/org/apache/dolphinscheduler/plugin/task/chunjun/ChunJunTaskChannel.java @@ -22,31 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; -/** - * chunjun task channel - */ public class ChunJunTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskExecutionContext) { return new ChunJunTask(taskExecutionContext); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), ChunJunParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, ChunJunParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, ChunJunParameters.class).getResources(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-chunjun/src/main/java/org/apache/dolphinscheduler/plugin/task/chunjun/ChunJunTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-chunjun/src/main/java/org/apache/dolphinscheduler/plugin/task/chunjun/ChunJunTaskChannelFactory.java index 06a0de36f1..b1465d723a 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-chunjun/src/main/java/org/apache/dolphinscheduler/plugin/task/chunjun/ChunJunTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-chunjun/src/main/java/org/apache/dolphinscheduler/plugin/task/chunjun/ChunJunTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.chunjun; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -47,13 +44,4 @@ public class ChunJunTaskChannelFactory implements TaskChannelFactory { return "CHUNJUN"; } - /** - * Returns the configurable parameters that this plugin needs to display on the web ui - * - * @return this plugin params - */ - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-datafactory/src/main/java/org/apache/dolphinscheduler/plugin/task/datafactory/DatafactoryTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-datafactory/src/main/java/org/apache/dolphinscheduler/plugin/task/datafactory/DatafactoryTaskChannel.java index d8c099bb09..b5062a3d4c 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-datafactory/src/main/java/org/apache/dolphinscheduler/plugin/task/datafactory/DatafactoryTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-datafactory/src/main/java/org/apache/dolphinscheduler/plugin/task/datafactory/DatafactoryTaskChannel.java @@ -21,27 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class DatafactoryTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - } - @Override public DatafactoryTask createTask(TaskExecutionContext taskRequest) { return new DatafactoryTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), DatafactoryParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DatafactoryParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-datafactory/src/main/java/org/apache/dolphinscheduler/plugin/task/datafactory/DatafactoryTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-datafactory/src/main/java/org/apache/dolphinscheduler/plugin/task/datafactory/DatafactoryTaskChannelFactory.java index e9dce1ac96..0ceb0ea2ed 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-datafactory/src/main/java/org/apache/dolphinscheduler/plugin/task/datafactory/DatafactoryTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-datafactory/src/main/java/org/apache/dolphinscheduler/plugin/task/datafactory/DatafactoryTaskChannelFactory.java @@ -19,10 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.datafactory; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.Collections; -import java.util.List; import com.google.auto.service.AutoService; @@ -39,8 +35,4 @@ public class DatafactoryTaskChannelFactory implements TaskChannelFactory { return "DATA_FACTORY"; } - @Override - public List getParams() { - return Collections.emptyList(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-dataquality/src/main/java/org/apache/dolphinscheduler/plugin/task/dq/DataQualityTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-dataquality/src/main/java/org/apache/dolphinscheduler/plugin/task/dq/DataQualityTaskChannel.java index b14596978c..1608de87af 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-dataquality/src/main/java/org/apache/dolphinscheduler/plugin/task/dq/DataQualityTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-dataquality/src/main/java/org/apache/dolphinscheduler/plugin/task/dq/DataQualityTaskChannel.java @@ -22,29 +22,18 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.plugin.task.api.parameters.dataquality.DataQualityParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class DataQualityTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new DataQualityTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), DataQualityParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DataQualityParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-dataquality/src/main/java/org/apache/dolphinscheduler/plugin/task/dq/DataQualityTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-dataquality/src/main/java/org/apache/dolphinscheduler/plugin/task/dq/DataQualityTaskChannelFactory.java index f56d65723f..7ad56daf13 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-dataquality/src/main/java/org/apache/dolphinscheduler/plugin/task/dq/DataQualityTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-dataquality/src/main/java/org/apache/dolphinscheduler/plugin/task/dq/DataQualityTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.dq; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class DataQualityTaskChannelFactory implements TaskChannelFactory { return "DATA_QUALITY"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new DataQualityTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-datasync/src/main/java/org/apache/dolphinscheduler/plugin/task/datasync/DatasyncTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-datasync/src/main/java/org/apache/dolphinscheduler/plugin/task/datasync/DatasyncTaskChannel.java index 1d4351caae..0e390c757e 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-datasync/src/main/java/org/apache/dolphinscheduler/plugin/task/datasync/DatasyncTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-datasync/src/main/java/org/apache/dolphinscheduler/plugin/task/datasync/DatasyncTaskChannel.java @@ -21,28 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class DatasyncTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public DatasyncTask createTask(TaskExecutionContext taskRequest) { return new DatasyncTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), DatasyncParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DatasyncParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-datasync/src/main/java/org/apache/dolphinscheduler/plugin/task/datasync/DatasyncTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-datasync/src/main/java/org/apache/dolphinscheduler/plugin/task/datasync/DatasyncTaskChannelFactory.java index 5fac501bea..770b07d3bb 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-datasync/src/main/java/org/apache/dolphinscheduler/plugin/task/datasync/DatasyncTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-datasync/src/main/java/org/apache/dolphinscheduler/plugin/task/datasync/DatasyncTaskChannelFactory.java @@ -19,10 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.datasync; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.Collections; -import java.util.List; import com.google.auto.service.AutoService; @@ -39,8 +35,4 @@ public class DatasyncTaskChannelFactory implements TaskChannelFactory { return "DATASYNC"; } - @Override - public List getParams() { - return Collections.emptyList(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-datax/src/main/java/org/apache/dolphinscheduler/plugin/task/datax/DataxTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-datax/src/main/java/org/apache/dolphinscheduler/plugin/task/datax/DataxTaskChannel.java index 5585b3d0b7..fd93e9c372 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-datax/src/main/java/org/apache/dolphinscheduler/plugin/task/datax/DataxTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-datax/src/main/java/org/apache/dolphinscheduler/plugin/task/datax/DataxTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class DataxTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new DataxTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), DataxParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DataxParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, DataxParameters.class).getResources(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-datax/src/main/java/org/apache/dolphinscheduler/plugin/task/datax/DataxTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-datax/src/main/java/org/apache/dolphinscheduler/plugin/task/datax/DataxTaskChannelFactory.java index 332d6bf708..2416081acf 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-datax/src/main/java/org/apache/dolphinscheduler/plugin/task/datax/DataxTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-datax/src/main/java/org/apache/dolphinscheduler/plugin/task/datax/DataxTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.datax; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class DataxTaskChannelFactory implements TaskChannelFactory { return "DATAX"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new DataxTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-dinky/src/main/java/org/apache/dolphinscheduler/plugin/task/dinky/DinkyTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-dinky/src/main/java/org/apache/dolphinscheduler/plugin/task/dinky/DinkyTaskChannel.java index 83846ff0fc..681d965fa8 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-dinky/src/main/java/org/apache/dolphinscheduler/plugin/task/dinky/DinkyTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-dinky/src/main/java/org/apache/dolphinscheduler/plugin/task/dinky/DinkyTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class DinkyTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - // nothing to do - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new DinkyTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), DinkyParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DinkyParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-dinky/src/main/java/org/apache/dolphinscheduler/plugin/task/dinky/DinkyTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-dinky/src/main/java/org/apache/dolphinscheduler/plugin/task/dinky/DinkyTaskChannelFactory.java index ddaa53d233..29f35b65e0 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-dinky/src/main/java/org/apache/dolphinscheduler/plugin/task/dinky/DinkyTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-dinky/src/main/java/org/apache/dolphinscheduler/plugin/task/dinky/DinkyTaskChannelFactory.java @@ -19,10 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.dinky; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.ArrayList; -import java.util.List; import com.google.auto.service.AutoService; @@ -34,11 +30,6 @@ public class DinkyTaskChannelFactory implements TaskChannelFactory { return "DINKY"; } - @Override - public List getParams() { - return new ArrayList<>(); - } - @Override public TaskChannel create() { return new DinkyTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-dms/src/main/java/org/apache/dolphinscheduler/plugin/task/dms/DmsTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-dms/src/main/java/org/apache/dolphinscheduler/plugin/task/dms/DmsTaskChannel.java index 45f26c5c1e..cb8d8893bd 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-dms/src/main/java/org/apache/dolphinscheduler/plugin/task/dms/DmsTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-dms/src/main/java/org/apache/dolphinscheduler/plugin/task/dms/DmsTaskChannel.java @@ -21,29 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class DmsTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public DmsTask createTask(TaskExecutionContext taskRequest) { return new DmsTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), DmsParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DmsParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-dms/src/main/java/org/apache/dolphinscheduler/plugin/task/dms/DmsTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-dms/src/main/java/org/apache/dolphinscheduler/plugin/task/dms/DmsTaskChannelFactory.java index a8564ff8a1..079e630d0c 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-dms/src/main/java/org/apache/dolphinscheduler/plugin/task/dms/DmsTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-dms/src/main/java/org/apache/dolphinscheduler/plugin/task/dms/DmsTaskChannelFactory.java @@ -19,10 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.dms; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.Collections; -import java.util.List; import com.google.auto.service.AutoService; @@ -39,8 +35,4 @@ public class DmsTaskChannelFactory implements TaskChannelFactory { return "DMS"; } - @Override - public List getParams() { - return Collections.emptyList(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-dvc/src/main/java/org/apache/dolphinscheduler/plugin/task/dvc/DvcTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-dvc/src/main/java/org/apache/dolphinscheduler/plugin/task/dvc/DvcTaskChannel.java index cf78414918..06f4421ca4 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-dvc/src/main/java/org/apache/dolphinscheduler/plugin/task/dvc/DvcTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-dvc/src/main/java/org/apache/dolphinscheduler/plugin/task/dvc/DvcTaskChannel.java @@ -21,29 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class DvcTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public DvcTask createTask(TaskExecutionContext taskRequest) { return new DvcTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), DvcParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, DvcParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-dvc/src/main/java/org/apache/dolphinscheduler/plugin/task/dvc/DvcTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-dvc/src/main/java/org/apache/dolphinscheduler/plugin/task/dvc/DvcTaskChannelFactory.java index a29ffded5b..b07612ad2e 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-dvc/src/main/java/org/apache/dolphinscheduler/plugin/task/dvc/DvcTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-dvc/src/main/java/org/apache/dolphinscheduler/plugin/task/dvc/DvcTaskChannelFactory.java @@ -19,14 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.dvc; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.ParamsOptions; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; -import org.apache.dolphinscheduler.spi.params.base.Validate; -import org.apache.dolphinscheduler.spi.params.input.InputParam; -import org.apache.dolphinscheduler.spi.params.radio.RadioParam; - -import java.util.ArrayList; -import java.util.List; import com.google.auto.service.AutoService; @@ -43,19 +35,4 @@ public class DvcTaskChannelFactory implements TaskChannelFactory { return "DVC"; } - @Override - public List getParams() { - List paramsList = new ArrayList<>(); - - InputParam nodeName = InputParam.newBuilder("name", "$t('Node name')") - .addValidate(Validate.newBuilder().setRequired(true).build()).build(); - - RadioParam runFlag = RadioParam.newBuilder("runFlag", "RUN_FLAG") - .addParamsOptions(new ParamsOptions("NORMAL", "NORMAL", false)) - .addParamsOptions(new ParamsOptions("FORBIDDEN", "FORBIDDEN", false)).build(); - - paramsList.add(nodeName); - paramsList.add(runFlag); - return paramsList; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-emr/src/main/java/org/apache/dolphinscheduler/plugin/task/emr/EmrTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-emr/src/main/java/org/apache/dolphinscheduler/plugin/task/emr/EmrTaskChannel.java index 8e42eb600f..80bb271ba4 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-emr/src/main/java/org/apache/dolphinscheduler/plugin/task/emr/EmrTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-emr/src/main/java/org/apache/dolphinscheduler/plugin/task/emr/EmrTaskChannel.java @@ -22,16 +22,9 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class EmrTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - // no need - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { EmrParameters emrParameters = JSONUtils.parseObject(taskRequest.getTaskParams(), EmrParameters.class); @@ -46,12 +39,8 @@ public class EmrTaskChannel implements TaskChannel { } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), EmrParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, EmrParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-emr/src/main/java/org/apache/dolphinscheduler/plugin/task/emr/EmrTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-emr/src/main/java/org/apache/dolphinscheduler/plugin/task/emr/EmrTaskChannelFactory.java index cad5583f07..606b0a64d8 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-emr/src/main/java/org/apache/dolphinscheduler/plugin/task/emr/EmrTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-emr/src/main/java/org/apache/dolphinscheduler/plugin/task/emr/EmrTaskChannelFactory.java @@ -19,10 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.emr; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.Collections; -import java.util.List; import com.google.auto.service.AutoService; @@ -34,11 +30,6 @@ public class EmrTaskChannelFactory implements TaskChannelFactory { return "EMR"; } - @Override - public List getParams() { - return Collections.emptyList(); - } - @Override public TaskChannel create() { return new EmrTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-flink-stream/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkStreamTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-flink-stream/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkStreamTaskChannel.java index 50f0984021..fd349e2abb 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-flink-stream/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkStreamTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-flink-stream/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkStreamTaskChannel.java @@ -18,42 +18,20 @@ package org.apache.dolphinscheduler.plugin.task.flink; import org.apache.dolphinscheduler.common.utils.JSONUtils; -import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; import org.apache.dolphinscheduler.plugin.task.api.stream.StreamTaskChannel; public class FlinkStreamTaskChannel implements StreamTaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public FlinkStreamTask createTask(TaskExecutionContext taskRequest) { return new FlinkStreamTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), FlinkStreamParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, FlinkStreamParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } - - @Override - public AbstractTask pauseTask(TaskExecutionContext taskExecutionContext) { - return null; - } - - @Override - public AbstractTask recoverTask(TaskExecutionContext taskExecutionContext) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-flink-stream/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkStreamTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-flink-stream/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkStreamTaskChannelFactory.java index 475386bf36..c75e7dbfb8 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-flink-stream/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkStreamTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-flink-stream/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkStreamTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.flink; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -38,8 +35,4 @@ public class FlinkStreamTaskChannelFactory implements TaskChannelFactory { return "FLINK_STREAM"; } - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-flink/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-flink/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkTaskChannel.java index ff11983f75..37a003c10c 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-flink/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-flink/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkTaskChannel.java @@ -21,28 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class FlinkTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public FlinkTask createTask(TaskExecutionContext taskRequest) { return new FlinkTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), FlinkParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, FlinkParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-flink/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-flink/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkTaskChannelFactory.java index 953f013fce..155e031d04 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-flink/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-flink/src/main/java/org/apache/dolphinscheduler/plugin/task/flink/FlinkTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.flink; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -38,8 +35,4 @@ public class FlinkTaskChannelFactory implements TaskChannelFactory { return "FLINK"; } - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-hivecli/src/main/java/org/apache/dolphinscheduler/plugin/task/hivecli/HiveCliTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-hivecli/src/main/java/org/apache/dolphinscheduler/plugin/task/hivecli/HiveCliTaskChannel.java index d45eae34e1..5397928ec0 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-hivecli/src/main/java/org/apache/dolphinscheduler/plugin/task/hivecli/HiveCliTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-hivecli/src/main/java/org/apache/dolphinscheduler/plugin/task/hivecli/HiveCliTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class HiveCliTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskExecutionContext) { return new HiveCliTask(taskExecutionContext); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), HiveCliParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, HiveCliParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-hivecli/src/main/java/org/apache/dolphinscheduler/plugin/task/hivecli/HiveCliTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-hivecli/src/main/java/org/apache/dolphinscheduler/plugin/task/hivecli/HiveCliTaskChannelFactory.java index 244ac2eba3..292a027fb4 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-hivecli/src/main/java/org/apache/dolphinscheduler/plugin/task/hivecli/HiveCliTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-hivecli/src/main/java/org/apache/dolphinscheduler/plugin/task/hivecli/HiveCliTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.hivecli; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -38,8 +35,4 @@ public class HiveCliTaskChannelFactory implements TaskChannelFactory { return "HIVECLI"; } - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-http/src/main/java/org/apache/dolphinscheduler/plugin/task/http/HttpTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-http/src/main/java/org/apache/dolphinscheduler/plugin/task/http/HttpTaskChannel.java index b91aa5bcb6..4589f5ac7e 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-http/src/main/java/org/apache/dolphinscheduler/plugin/task/http/HttpTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-http/src/main/java/org/apache/dolphinscheduler/plugin/task/http/HttpTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class HttpTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new HttpTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), HttpParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, HttpParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-http/src/main/java/org/apache/dolphinscheduler/plugin/task/http/HttpTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-http/src/main/java/org/apache/dolphinscheduler/plugin/task/http/HttpTaskChannelFactory.java index efbc254b0d..e7743cfe19 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-http/src/main/java/org/apache/dolphinscheduler/plugin/task/http/HttpTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-http/src/main/java/org/apache/dolphinscheduler/plugin/task/http/HttpTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.http; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class HttpTaskChannelFactory implements TaskChannelFactory { return "HTTP"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new HttpTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannel.java index 710342ed13..26f04f0c40 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannel.java @@ -21,52 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class JavaTaskChannel implements TaskChannel { - /** - * Cancel the mission - * - * @param status - * @return void - **/ - @Override - public void cancelApplication(boolean status) { - - } - - /** - * Create a task - * - * @param taskRequest This parameter is the Echternach of the mission - * @return JavaTask - **/ @Override public JavaTask createTask(TaskExecutionContext taskRequest) { return new JavaTask(taskRequest); } - /** - * Parses Java task parameters - * - * @param parametersNode - * @return: org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters - **/ @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), JavaParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, JavaParameters.class); } - /** - * Gets a list of the resources that the task depends on - * - * @param parameters - * @return ResourceParametersHelper - **/ - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannelFactory.java index e72f62e29e..4db03756d4 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-java/src/main/java/org/apache/dolphinscheduler/plugin/task/java/JavaTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.java; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -48,13 +45,4 @@ public class JavaTaskChannelFactory implements TaskChannelFactory { return "JAVA"; } - /** - * Gets the plug-in parameters for the Java task - * - * @return List - **/ - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-jupyter/src/main/java/org/apache/dolphinscheduler/plugin/task/jupyter/JupyterTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-jupyter/src/main/java/org/apache/dolphinscheduler/plugin/task/jupyter/JupyterTaskChannel.java index c98625c89e..a93e77a827 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-jupyter/src/main/java/org/apache/dolphinscheduler/plugin/task/jupyter/JupyterTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-jupyter/src/main/java/org/apache/dolphinscheduler/plugin/task/jupyter/JupyterTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class JupyterTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new JupyterTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), JupyterParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, JupyterParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-jupyter/src/main/java/org/apache/dolphinscheduler/plugin/task/jupyter/JupyterTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-jupyter/src/main/java/org/apache/dolphinscheduler/plugin/task/jupyter/JupyterTaskChannelFactory.java index 41bff2bf80..ba2709d388 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-jupyter/src/main/java/org/apache/dolphinscheduler/plugin/task/jupyter/JupyterTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-jupyter/src/main/java/org/apache/dolphinscheduler/plugin/task/jupyter/JupyterTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.jupyter; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class JupyterTaskChannelFactory implements TaskChannelFactory { return "JUPYTER"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new JupyterTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-k8s/src/main/java/org/apache/dolphinscheduler/plugin/task/k8s/K8sTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-k8s/src/main/java/org/apache/dolphinscheduler/plugin/task/k8s/K8sTaskChannel.java index 91c0faf684..b512201edd 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-k8s/src/main/java/org/apache/dolphinscheduler/plugin/task/k8s/K8sTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-k8s/src/main/java/org/apache/dolphinscheduler/plugin/task/k8s/K8sTaskChannel.java @@ -23,24 +23,12 @@ import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; import org.apache.dolphinscheduler.plugin.task.api.parameters.K8sTaskParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class K8sTaskChannel implements TaskChannel { @Override - public void cancelApplication(boolean status) { - - } - - @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), K8sTaskParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, K8sTaskParameters.class).getResources(); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, K8sTaskParameters.class); } @Override diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-k8s/src/main/java/org/apache/dolphinscheduler/plugin/task/k8s/K8sTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-k8s/src/main/java/org/apache/dolphinscheduler/plugin/task/k8s/K8sTaskChannelFactory.java index 4af560b5d2..b767c4bd33 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-k8s/src/main/java/org/apache/dolphinscheduler/plugin/task/k8s/K8sTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-k8s/src/main/java/org/apache/dolphinscheduler/plugin/task/k8s/K8sTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.k8s; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class K8sTaskChannelFactory implements TaskChannelFactory { return "K8S"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new K8sTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-kubeflow/src/main/java/org/apache/dolphinscheduler/plugin/kubeflow/KubeflowTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-kubeflow/src/main/java/org/apache/dolphinscheduler/plugin/kubeflow/KubeflowTaskChannel.java index 5bfdc9e258..88e388c9f9 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-kubeflow/src/main/java/org/apache/dolphinscheduler/plugin/kubeflow/KubeflowTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-kubeflow/src/main/java/org/apache/dolphinscheduler/plugin/kubeflow/KubeflowTaskChannel.java @@ -21,28 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class KubeflowTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public KubeflowTask createTask(TaskExecutionContext taskRequest) { return new KubeflowTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), KubeflowParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, KubeflowParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-kubeflow/src/main/java/org/apache/dolphinscheduler/plugin/kubeflow/KubeflowTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-kubeflow/src/main/java/org/apache/dolphinscheduler/plugin/kubeflow/KubeflowTaskChannelFactory.java index 9481311ce6..1a18cc03b0 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-kubeflow/src/main/java/org/apache/dolphinscheduler/plugin/kubeflow/KubeflowTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-kubeflow/src/main/java/org/apache/dolphinscheduler/plugin/kubeflow/KubeflowTaskChannelFactory.java @@ -19,10 +19,6 @@ package org.apache.dolphinscheduler.plugin.kubeflow; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.Collections; -import java.util.List; import com.google.auto.service.AutoService; @@ -39,9 +35,4 @@ public class KubeflowTaskChannelFactory implements TaskChannelFactory { return "KUBEFLOW"; } - @Override - public List getParams() { - return Collections.emptyList(); - } - } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-linkis/src/main/java/org/apache/dolphinscheduler/plugin/task/linkis/LinkisTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-linkis/src/main/java/org/apache/dolphinscheduler/plugin/task/linkis/LinkisTaskChannel.java index b5d357a45e..d49006dd6d 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-linkis/src/main/java/org/apache/dolphinscheduler/plugin/task/linkis/LinkisTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-linkis/src/main/java/org/apache/dolphinscheduler/plugin/task/linkis/LinkisTaskChannel.java @@ -21,29 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class LinkisTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public LinkisTask createTask(TaskExecutionContext taskRequest) { return new LinkisTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), LinkisParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, LinkisParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-linkis/src/main/java/org/apache/dolphinscheduler/plugin/task/linkis/LinkisTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-linkis/src/main/java/org/apache/dolphinscheduler/plugin/task/linkis/LinkisTaskChannelFactory.java index 5d2c0d563e..a3bc274234 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-linkis/src/main/java/org/apache/dolphinscheduler/plugin/task/linkis/LinkisTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-linkis/src/main/java/org/apache/dolphinscheduler/plugin/task/linkis/LinkisTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.linkis; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -38,8 +35,4 @@ public class LinkisTaskChannelFactory implements TaskChannelFactory { return "LINKIS"; } - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-mlflow/src/main/java/org/apache/dolphinscheduler/plugin/task/mlflow/MlflowTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-mlflow/src/main/java/org/apache/dolphinscheduler/plugin/task/mlflow/MlflowTaskChannel.java index 7dfacd0006..a2687c2634 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-mlflow/src/main/java/org/apache/dolphinscheduler/plugin/task/mlflow/MlflowTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-mlflow/src/main/java/org/apache/dolphinscheduler/plugin/task/mlflow/MlflowTaskChannel.java @@ -21,29 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class MlflowTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public MlflowTask createTask(TaskExecutionContext taskRequest) { return new MlflowTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), MlflowParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, MlflowParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-mlflow/src/main/java/org/apache/dolphinscheduler/plugin/task/mlflow/MlflowTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-mlflow/src/main/java/org/apache/dolphinscheduler/plugin/task/mlflow/MlflowTaskChannelFactory.java index dc18814d1d..e4985d42f2 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-mlflow/src/main/java/org/apache/dolphinscheduler/plugin/task/mlflow/MlflowTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-mlflow/src/main/java/org/apache/dolphinscheduler/plugin/task/mlflow/MlflowTaskChannelFactory.java @@ -19,14 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.mlflow; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.ParamsOptions; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; -import org.apache.dolphinscheduler.spi.params.base.Validate; -import org.apache.dolphinscheduler.spi.params.input.InputParam; -import org.apache.dolphinscheduler.spi.params.radio.RadioParam; - -import java.util.ArrayList; -import java.util.List; import com.google.auto.service.AutoService; @@ -43,19 +35,4 @@ public class MlflowTaskChannelFactory implements TaskChannelFactory { return "MLFLOW"; } - @Override - public List getParams() { - List paramsList = new ArrayList<>(); - - InputParam nodeName = InputParam.newBuilder("name", "$t('Node name')") - .addValidate(Validate.newBuilder().setRequired(true).build()).build(); - - RadioParam runFlag = RadioParam.newBuilder("runFlag", "RUN_FLAG") - .addParamsOptions(new ParamsOptions("NORMAL", "NORMAL", false)) - .addParamsOptions(new ParamsOptions("FORBIDDEN", "FORBIDDEN", false)).build(); - - paramsList.add(nodeName); - paramsList.add(runFlag); - return paramsList; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-mr/src/main/java/org/apache/dolphinscheduler/plugin/task/mr/MapReduceTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-mr/src/main/java/org/apache/dolphinscheduler/plugin/task/mr/MapReduceTaskChannel.java index 9dc9273769..525376cd9b 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-mr/src/main/java/org/apache/dolphinscheduler/plugin/task/mr/MapReduceTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-mr/src/main/java/org/apache/dolphinscheduler/plugin/task/mr/MapReduceTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class MapReduceTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new MapReduceTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), MapReduceParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, MapReduceParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-mr/src/main/java/org/apache/dolphinscheduler/plugin/task/mr/MapReduceTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-mr/src/main/java/org/apache/dolphinscheduler/plugin/task/mr/MapReduceTaskChannelFactory.java index e42737d83e..87951d715b 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-mr/src/main/java/org/apache/dolphinscheduler/plugin/task/mr/MapReduceTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-mr/src/main/java/org/apache/dolphinscheduler/plugin/task/mr/MapReduceTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.mr; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class MapReduceTaskChannelFactory implements TaskChannelFactory { return "MR"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new MapReduceTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-openmldb/src/main/java/org/apache/dolphinscheduler/plugin/task/openmldb/OpenmldbTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-openmldb/src/main/java/org/apache/dolphinscheduler/plugin/task/openmldb/OpenmldbTaskChannel.java index b4e7ca8e8d..aab2a4b7a1 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-openmldb/src/main/java/org/apache/dolphinscheduler/plugin/task/openmldb/OpenmldbTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-openmldb/src/main/java/org/apache/dolphinscheduler/plugin/task/openmldb/OpenmldbTaskChannel.java @@ -21,28 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class OpenmldbTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public OpenmldbTask createTask(TaskExecutionContext taskRequest) { return new OpenmldbTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), OpenmldbParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, OpenmldbParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-openmldb/src/main/java/org/apache/dolphinscheduler/plugin/task/openmldb/OpenmldbTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-openmldb/src/main/java/org/apache/dolphinscheduler/plugin/task/openmldb/OpenmldbTaskChannelFactory.java index 32e3eaa04d..7a0e8d5a3a 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-openmldb/src/main/java/org/apache/dolphinscheduler/plugin/task/openmldb/OpenmldbTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-openmldb/src/main/java/org/apache/dolphinscheduler/plugin/task/openmldb/OpenmldbTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.openmldb; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -38,8 +35,4 @@ public class OpenmldbTaskChannelFactory implements TaskChannelFactory { return "OPENMLDB"; } - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-pigeon/src/main/java/org/apache/dolphinscheduler/plugin/task/pigeon/PigeonTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-pigeon/src/main/java/org/apache/dolphinscheduler/plugin/task/pigeon/PigeonTaskChannel.java index 9a1c20fcd1..112f087a57 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-pigeon/src/main/java/org/apache/dolphinscheduler/plugin/task/pigeon/PigeonTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-pigeon/src/main/java/org/apache/dolphinscheduler/plugin/task/pigeon/PigeonTaskChannel.java @@ -22,31 +22,20 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; import lombok.extern.slf4j.Slf4j; @Slf4j public class PigeonTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - log.info("pigeon task cancel"); - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new PigeonTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), PigeonParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, PigeonParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-pigeon/src/main/java/org/apache/dolphinscheduler/plugin/task/pigeon/PigeonTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-pigeon/src/main/java/org/apache/dolphinscheduler/plugin/task/pigeon/PigeonTaskChannelFactory.java index a7ce316921..584f131a42 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-pigeon/src/main/java/org/apache/dolphinscheduler/plugin/task/pigeon/PigeonTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-pigeon/src/main/java/org/apache/dolphinscheduler/plugin/task/pigeon/PigeonTaskChannelFactory.java @@ -19,12 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.pigeon; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; -import org.apache.dolphinscheduler.spi.params.base.Validate; -import org.apache.dolphinscheduler.spi.params.input.InputParam; - -import java.util.Arrays; -import java.util.List; import com.google.auto.service.AutoService; @@ -41,12 +35,4 @@ public class PigeonTaskChannelFactory implements TaskChannelFactory { return "PIGEON"; } - @Override - public List getParams() { - InputParam webHookParam = - InputParam.newBuilder(PigeonParamsConstants.NAME_TARGET_JOB_NAME, PigeonParamsConstants.TARGET_JOB_NAME) - .addValidate(Validate.newBuilder().setRequired(true).build()) - .build(); - return Arrays.asList(webHookParam); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-procedure/src/main/java/org/apache/dolphinscheduler/plugin/task/procedure/ProcedureTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-procedure/src/main/java/org/apache/dolphinscheduler/plugin/task/procedure/ProcedureTaskChannel.java index ccad7d0c4f..7138cddc89 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-procedure/src/main/java/org/apache/dolphinscheduler/plugin/task/procedure/ProcedureTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-procedure/src/main/java/org/apache/dolphinscheduler/plugin/task/procedure/ProcedureTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class ProcedureTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new ProcedureTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), ProcedureParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, ProcedureParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, ProcedureParameters.class).getResources(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-procedure/src/main/java/org/apache/dolphinscheduler/plugin/task/procedure/ProcedureTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-procedure/src/main/java/org/apache/dolphinscheduler/plugin/task/procedure/ProcedureTaskChannelFactory.java index a96a082af0..4da62ab5bf 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-procedure/src/main/java/org/apache/dolphinscheduler/plugin/task/procedure/ProcedureTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-procedure/src/main/java/org/apache/dolphinscheduler/plugin/task/procedure/ProcedureTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.procedure; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class ProcedureTaskChannelFactory implements TaskChannelFactory { return "PROCEDURE"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new ProcedureTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-python/src/main/java/org/apache/dolphinscheduler/plugin/task/python/PythonTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-python/src/main/java/org/apache/dolphinscheduler/plugin/task/python/PythonTaskChannel.java index d602d4e162..a7a1131a50 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-python/src/main/java/org/apache/dolphinscheduler/plugin/task/python/PythonTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-python/src/main/java/org/apache/dolphinscheduler/plugin/task/python/PythonTaskChannel.java @@ -21,28 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class PythonTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public PythonTask createTask(TaskExecutionContext taskRequest) { return new PythonTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), PythonParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, PythonParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-python/src/main/java/org/apache/dolphinscheduler/plugin/task/python/PythonTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-python/src/main/java/org/apache/dolphinscheduler/plugin/task/python/PythonTaskChannelFactory.java index ddd969f198..214a3a9b25 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-python/src/main/java/org/apache/dolphinscheduler/plugin/task/python/PythonTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-python/src/main/java/org/apache/dolphinscheduler/plugin/task/python/PythonTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.python; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -38,8 +35,4 @@ public class PythonTaskChannelFactory implements TaskChannelFactory { return "PYTHON"; } - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-pytorch/src/main/java/org/apache/dolphinscheduler/plugin/task/pytorch/PytorchTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-pytorch/src/main/java/org/apache/dolphinscheduler/plugin/task/pytorch/PytorchTaskChannel.java index 6d2427e594..5d4f7a63f4 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-pytorch/src/main/java/org/apache/dolphinscheduler/plugin/task/pytorch/PytorchTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-pytorch/src/main/java/org/apache/dolphinscheduler/plugin/task/pytorch/PytorchTaskChannel.java @@ -21,29 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class PytorchTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public PytorchTask createTask(TaskExecutionContext taskRequest) { return new PytorchTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), PytorchParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, PytorchParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-pytorch/src/main/java/org/apache/dolphinscheduler/plugin/task/pytorch/PytorchTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-pytorch/src/main/java/org/apache/dolphinscheduler/plugin/task/pytorch/PytorchTaskChannelFactory.java index bf1bda8b2d..7b4fd09b59 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-pytorch/src/main/java/org/apache/dolphinscheduler/plugin/task/pytorch/PytorchTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-pytorch/src/main/java/org/apache/dolphinscheduler/plugin/task/pytorch/PytorchTaskChannelFactory.java @@ -19,10 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.pytorch; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.Collections; -import java.util.List; import com.google.auto.service.AutoService; @@ -39,8 +35,4 @@ public class PytorchTaskChannelFactory implements TaskChannelFactory { return "PYTORCH"; } - @Override - public List getParams() { - return Collections.emptyList(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-remoteshell/src/main/java/org/apache/dolphinscheduler/plugin/task/remoteshell/RemoteShellTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-remoteshell/src/main/java/org/apache/dolphinscheduler/plugin/task/remoteshell/RemoteShellTaskChannel.java index e2baeca3d1..7143aa4ff7 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-remoteshell/src/main/java/org/apache/dolphinscheduler/plugin/task/remoteshell/RemoteShellTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-remoteshell/src/main/java/org/apache/dolphinscheduler/plugin/task/remoteshell/RemoteShellTaskChannel.java @@ -21,28 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class RemoteShellTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public RemoteShellTask createTask(TaskExecutionContext taskRequest) { return new RemoteShellTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), RemoteShellParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, RemoteShellParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, RemoteShellParameters.class).getResources(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-remoteshell/src/main/java/org/apache/dolphinscheduler/plugin/task/remoteshell/RemoteShellTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-remoteshell/src/main/java/org/apache/dolphinscheduler/plugin/task/remoteshell/RemoteShellTaskChannelFactory.java index 394a0aae76..2f51f83cc3 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-remoteshell/src/main/java/org/apache/dolphinscheduler/plugin/task/remoteshell/RemoteShellTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-remoteshell/src/main/java/org/apache/dolphinscheduler/plugin/task/remoteshell/RemoteShellTaskChannelFactory.java @@ -19,14 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.remoteshell; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.ParamsOptions; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; -import org.apache.dolphinscheduler.spi.params.base.Validate; -import org.apache.dolphinscheduler.spi.params.input.InputParam; -import org.apache.dolphinscheduler.spi.params.radio.RadioParam; - -import java.util.ArrayList; -import java.util.List; import com.google.auto.service.AutoService; @@ -43,23 +35,4 @@ public class RemoteShellTaskChannelFactory implements TaskChannelFactory { return "REMOTESHELL"; } - @Override - public List getParams() { - List paramsList = new ArrayList<>(); - - InputParam nodeName = InputParam.newBuilder("name", "$t('Node name')") - .addValidate(Validate.newBuilder() - .setRequired(true) - .build()) - .build(); - - RadioParam runFlag = RadioParam.newBuilder("runFlag", "RUN_FLAG") - .addParamsOptions(new ParamsOptions("NORMAL", "NORMAL", false)) - .addParamsOptions(new ParamsOptions("FORBIDDEN", "FORBIDDEN", false)) - .build(); - - paramsList.add(nodeName); - paramsList.add(runFlag); - return paramsList; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-sagemaker/src/main/java/org/apache/dolphinscheduler/plugin/task/sagemaker/SagemakerTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-sagemaker/src/main/java/org/apache/dolphinscheduler/plugin/task/sagemaker/SagemakerTaskChannel.java index 4dcdb051e9..926d23df05 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-sagemaker/src/main/java/org/apache/dolphinscheduler/plugin/task/sagemaker/SagemakerTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-sagemaker/src/main/java/org/apache/dolphinscheduler/plugin/task/sagemaker/SagemakerTaskChannel.java @@ -21,29 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class SagemakerTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public SagemakerTask createTask(TaskExecutionContext taskRequest) { return new SagemakerTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), SagemakerParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, SagemakerParameters.class).getResources(); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, SagemakerParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-sagemaker/src/main/java/org/apache/dolphinscheduler/plugin/task/sagemaker/SagemakerTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-sagemaker/src/main/java/org/apache/dolphinscheduler/plugin/task/sagemaker/SagemakerTaskChannelFactory.java index ccad195e0e..370f8beacd 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-sagemaker/src/main/java/org/apache/dolphinscheduler/plugin/task/sagemaker/SagemakerTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-sagemaker/src/main/java/org/apache/dolphinscheduler/plugin/task/sagemaker/SagemakerTaskChannelFactory.java @@ -19,10 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.sagemaker; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.Collections; -import java.util.List; import com.google.auto.service.AutoService; @@ -39,8 +35,4 @@ public class SagemakerTaskChannelFactory implements TaskChannelFactory { return "SAGEMAKER"; } - @Override - public List getParams() { - return Collections.emptyList(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-seatunnel/src/main/java/org/apache/dolphinscheduler/plugin/task/seatunnel/SeatunnelTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-seatunnel/src/main/java/org/apache/dolphinscheduler/plugin/task/seatunnel/SeatunnelTaskChannel.java index ae01d8edd8..9632f8051a 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-seatunnel/src/main/java/org/apache/dolphinscheduler/plugin/task/seatunnel/SeatunnelTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-seatunnel/src/main/java/org/apache/dolphinscheduler/plugin/task/seatunnel/SeatunnelTaskChannel.java @@ -25,19 +25,12 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; import org.apache.dolphinscheduler.plugin.task.seatunnel.flink.SeatunnelFlinkTask; import org.apache.dolphinscheduler.plugin.task.seatunnel.self.SeatunnelEngineTask; import org.apache.dolphinscheduler.plugin.task.seatunnel.spark.SeatunnelSparkTask; public class SeatunnelTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public SeatunnelTask createTask(TaskExecutionContext taskRequest) { SeatunnelParameters seatunnelParameters = @@ -57,13 +50,8 @@ public class SeatunnelTaskChannel implements TaskChannel { } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), SeatunnelParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, SeatunnelParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-seatunnel/src/main/java/org/apache/dolphinscheduler/plugin/task/seatunnel/SeatunnelTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-seatunnel/src/main/java/org/apache/dolphinscheduler/plugin/task/seatunnel/SeatunnelTaskChannelFactory.java index 2763c00d50..ab7b3f0cf1 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-seatunnel/src/main/java/org/apache/dolphinscheduler/plugin/task/seatunnel/SeatunnelTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-seatunnel/src/main/java/org/apache/dolphinscheduler/plugin/task/seatunnel/SeatunnelTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.seatunnel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -38,8 +35,4 @@ public class SeatunnelTaskChannelFactory implements TaskChannelFactory { return "SEATUNNEL"; } - @Override - public List getParams() { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-shell/src/main/java/org/apache/dolphinscheduler/plugin/task/shell/ShellTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-shell/src/main/java/org/apache/dolphinscheduler/plugin/task/shell/ShellTaskChannel.java index 12f0263aa1..26c1019700 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-shell/src/main/java/org/apache/dolphinscheduler/plugin/task/shell/ShellTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-shell/src/main/java/org/apache/dolphinscheduler/plugin/task/shell/ShellTaskChannel.java @@ -21,29 +21,17 @@ import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class ShellTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public ShellTask createTask(TaskExecutionContext taskRequest) { return new ShellTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), ShellParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, ShellParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-shell/src/main/java/org/apache/dolphinscheduler/plugin/task/shell/ShellTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-shell/src/main/java/org/apache/dolphinscheduler/plugin/task/shell/ShellTaskChannelFactory.java index 801826ffd1..560bae5b1d 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-shell/src/main/java/org/apache/dolphinscheduler/plugin/task/shell/ShellTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-shell/src/main/java/org/apache/dolphinscheduler/plugin/task/shell/ShellTaskChannelFactory.java @@ -19,20 +19,14 @@ package org.apache.dolphinscheduler.plugin.task.shell; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.ParamsOptions; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; -import org.apache.dolphinscheduler.spi.params.base.Validate; -import org.apache.dolphinscheduler.spi.params.input.InputParam; -import org.apache.dolphinscheduler.spi.params.radio.RadioParam; - -import java.util.ArrayList; -import java.util.List; import com.google.auto.service.AutoService; @AutoService(TaskChannelFactory.class) public class ShellTaskChannelFactory implements TaskChannelFactory { + public static final String NAME = "SHELL"; + @Override public TaskChannel create() { return new ShellTaskChannel(); @@ -40,26 +34,7 @@ public class ShellTaskChannelFactory implements TaskChannelFactory { @Override public String getName() { - return "SHELL"; + return NAME; } - @Override - public List getParams() { - List paramsList = new ArrayList<>(); - - InputParam nodeName = InputParam.newBuilder("name", "$t('Node name')") - .addValidate(Validate.newBuilder() - .setRequired(true) - .build()) - .build(); - - RadioParam runFlag = RadioParam.newBuilder("runFlag", "RUN_FLAG") - .addParamsOptions(new ParamsOptions("NORMAL", "NORMAL", false)) - .addParamsOptions(new ParamsOptions("FORBIDDEN", "FORBIDDEN", false)) - .build(); - - paramsList.add(nodeName); - paramsList.add(runFlag); - return paramsList; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-spark/src/main/java/org/apache/dolphinscheduler/plugin/task/spark/SparkTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-spark/src/main/java/org/apache/dolphinscheduler/plugin/task/spark/SparkTaskChannel.java index 58211ea1ac..b34ab30674 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-spark/src/main/java/org/apache/dolphinscheduler/plugin/task/spark/SparkTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-spark/src/main/java/org/apache/dolphinscheduler/plugin/task/spark/SparkTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class SparkTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new SparkTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), SparkParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, SparkParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return null; - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-spark/src/main/java/org/apache/dolphinscheduler/plugin/task/spark/SparkTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-spark/src/main/java/org/apache/dolphinscheduler/plugin/task/spark/SparkTaskChannelFactory.java index 92db56ad28..ec6d371b01 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-spark/src/main/java/org/apache/dolphinscheduler/plugin/task/spark/SparkTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-spark/src/main/java/org/apache/dolphinscheduler/plugin/task/spark/SparkTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.spark; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class SparkTaskChannelFactory implements TaskChannelFactory { return "SPARK"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new SparkTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-sql/src/main/java/org/apache/dolphinscheduler/plugin/task/sql/SqlTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-sql/src/main/java/org/apache/dolphinscheduler/plugin/task/sql/SqlTaskChannel.java index 79b4098a9c..5b40725669 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-sql/src/main/java/org/apache/dolphinscheduler/plugin/task/sql/SqlTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-sql/src/main/java/org/apache/dolphinscheduler/plugin/task/sql/SqlTaskChannel.java @@ -22,30 +22,18 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.plugin.task.api.parameters.SqlParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class SqlTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new SqlTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), SqlParameters.class); - } - - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, SqlParameters.class).getResources(); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, SqlParameters.class); } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-sql/src/main/java/org/apache/dolphinscheduler/plugin/task/sql/SqlTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-sql/src/main/java/org/apache/dolphinscheduler/plugin/task/sql/SqlTaskChannelFactory.java index 909741edfc..9627515673 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-sql/src/main/java/org/apache/dolphinscheduler/plugin/task/sql/SqlTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-sql/src/main/java/org/apache/dolphinscheduler/plugin/task/sql/SqlTaskChannelFactory.java @@ -19,23 +19,17 @@ package org.apache.dolphinscheduler.plugin.task.sql; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @AutoService(TaskChannelFactory.class) public class SqlTaskChannelFactory implements TaskChannelFactory { - @Override - public String getName() { - return "SQL"; - } + public static final String NAME = "SQL"; @Override - public List getParams() { - return null; + public String getName() { + return NAME; } @Override diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-sqoop/src/main/java/org/apache/dolphinscheduler/plugin/task/sqoop/SqoopTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-sqoop/src/main/java/org/apache/dolphinscheduler/plugin/task/sqoop/SqoopTaskChannel.java index 098cc3dc01..a22281eca0 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-sqoop/src/main/java/org/apache/dolphinscheduler/plugin/task/sqoop/SqoopTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-sqoop/src/main/java/org/apache/dolphinscheduler/plugin/task/sqoop/SqoopTaskChannel.java @@ -22,28 +22,18 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; import org.apache.dolphinscheduler.plugin.task.sqoop.parameter.SqoopParameters; public class SqoopTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new SqoopTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), SqoopParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, SqoopParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, SqoopParameters.class).getResources(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-sqoop/src/main/java/org/apache/dolphinscheduler/plugin/task/sqoop/SqoopTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-sqoop/src/main/java/org/apache/dolphinscheduler/plugin/task/sqoop/SqoopTaskChannelFactory.java index 6879af6a6e..f4340c01d5 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-sqoop/src/main/java/org/apache/dolphinscheduler/plugin/task/sqoop/SqoopTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-sqoop/src/main/java/org/apache/dolphinscheduler/plugin/task/sqoop/SqoopTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.sqoop; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class SqoopTaskChannelFactory implements TaskChannelFactory { return "SQOOP"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new SqoopTaskChannel(); diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-zeppelin/src/main/java/org/apache/dolphinscheduler/plugin/task/zeppelin/ZeppelinTaskChannel.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-zeppelin/src/main/java/org/apache/dolphinscheduler/plugin/task/zeppelin/ZeppelinTaskChannel.java index d9e7318dc4..a784a37498 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-zeppelin/src/main/java/org/apache/dolphinscheduler/plugin/task/zeppelin/ZeppelinTaskChannel.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-zeppelin/src/main/java/org/apache/dolphinscheduler/plugin/task/zeppelin/ZeppelinTaskChannel.java @@ -22,28 +22,17 @@ import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; -import org.apache.dolphinscheduler.plugin.task.api.parameters.resource.ResourceParametersHelper; public class ZeppelinTaskChannel implements TaskChannel { - @Override - public void cancelApplication(boolean status) { - - } - @Override public AbstractTask createTask(TaskExecutionContext taskRequest) { return new ZeppelinTask(taskRequest); } @Override - public AbstractParameters parseParameters(ParametersNode parametersNode) { - return JSONUtils.parseObject(parametersNode.getTaskParams(), ZeppelinParameters.class); + public AbstractParameters parseParameters(String taskParams) { + return JSONUtils.parseObject(taskParams, ZeppelinParameters.class); } - @Override - public ResourceParametersHelper getResources(String parameters) { - return JSONUtils.parseObject(parameters, ZeppelinParameters.class).getResources(); - } } diff --git a/dolphinscheduler-task-plugin/dolphinscheduler-task-zeppelin/src/main/java/org/apache/dolphinscheduler/plugin/task/zeppelin/ZeppelinTaskChannelFactory.java b/dolphinscheduler-task-plugin/dolphinscheduler-task-zeppelin/src/main/java/org/apache/dolphinscheduler/plugin/task/zeppelin/ZeppelinTaskChannelFactory.java index 006735875c..1afea163bc 100644 --- a/dolphinscheduler-task-plugin/dolphinscheduler-task-zeppelin/src/main/java/org/apache/dolphinscheduler/plugin/task/zeppelin/ZeppelinTaskChannelFactory.java +++ b/dolphinscheduler-task-plugin/dolphinscheduler-task-zeppelin/src/main/java/org/apache/dolphinscheduler/plugin/task/zeppelin/ZeppelinTaskChannelFactory.java @@ -19,9 +19,6 @@ package org.apache.dolphinscheduler.plugin.task.zeppelin; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskChannelFactory; -import org.apache.dolphinscheduler.spi.params.base.PluginParams; - -import java.util.List; import com.google.auto.service.AutoService; @@ -33,11 +30,6 @@ public class ZeppelinTaskChannelFactory implements TaskChannelFactory { return "ZEPPELIN"; } - @Override - public List getParams() { - return null; - } - @Override public TaskChannel create() { return new ZeppelinTaskChannel(); diff --git a/dolphinscheduler-tools/src/main/java/org/apache/dolphinscheduler/tools/datasource/upgrader/v200/V200DolphinSchedulerUpgrader.java b/dolphinscheduler-tools/src/main/java/org/apache/dolphinscheduler/tools/datasource/upgrader/v200/V200DolphinSchedulerUpgrader.java index fd430c2b06..a90e4dc6b1 100644 --- a/dolphinscheduler-tools/src/main/java/org/apache/dolphinscheduler/tools/datasource/upgrader/v200/V200DolphinSchedulerUpgrader.java +++ b/dolphinscheduler-tools/src/main/java/org/apache/dolphinscheduler/tools/datasource/upgrader/v200/V200DolphinSchedulerUpgrader.java @@ -17,10 +17,6 @@ package org.apache.dolphinscheduler.tools.datasource.upgrader.v200; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_CONDITIONS; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_DEPENDENT; -import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SUB_PROCESS; - import org.apache.dolphinscheduler.common.constants.Constants; import org.apache.dolphinscheduler.common.enums.ConditionType; import org.apache.dolphinscheduler.common.enums.Flag; @@ -171,7 +167,7 @@ public class V200DolphinSchedulerUpgrader implements DolphinSchedulerUpgrader { } else { taskDefinitionLog.setResourceIds(""); } - if (TASK_TYPE_SUB_PROCESS.equals(taskType)) { + if ("SUB_PROCESS".equals(taskType)) { JsonNode jsonNodeDefinitionId = param.get("processDefinitionId"); if (jsonNodeDefinitionId != null) { param.put("processDefinitionCode", @@ -197,9 +193,9 @@ public class V200DolphinSchedulerUpgrader implements DolphinSchedulerUpgrader { Constants.FLOWNODE_RUN_FLAG_NORMAL.equals(task.get("runFlag").asText()) ? Flag.YES : Flag.NO); taskDefinitionLog.setTaskType(taskType); taskDefinitionLog.setFailRetryInterval( - TASK_TYPE_SUB_PROCESS.equals(taskType) ? 1 : task.get("retryInterval").asInt()); + "SUB_PROCESS".equals(taskType) ? 1 : task.get("retryInterval").asInt()); taskDefinitionLog.setFailRetryTimes( - TASK_TYPE_SUB_PROCESS.equals(taskType) ? 0 : task.get("maxRetryTimes").asInt()); + "SUB_PROCESS".equals(taskType) ? 0 : task.get("maxRetryTimes").asInt()); taskDefinitionLog.setTaskPriority(JSONUtils .parseObject(JSONUtils.toJsonString(task.get("taskInstancePriority")), Priority.class)); String name = task.get("name").asText(); @@ -241,7 +237,7 @@ public class V200DolphinSchedulerUpgrader implements DolphinSchedulerUpgrader { Map projectIdCodeMap, Map>> processTaskMap) { for (TaskDefinitionLog taskDefinitionLog : taskDefinitionLogs) { - if (TASK_TYPE_DEPENDENT.equals(taskDefinitionLog.getTaskType())) { + if ("DEPENDENT".equals(taskDefinitionLog.getTaskType())) { ObjectNode taskParams = JSONUtils.parseObject(taskDefinitionLog.getTaskParams()); ObjectNode dependence = (ObjectNode) taskParams.get("dependence"); ArrayNode dependTaskList = @@ -290,7 +286,7 @@ public class V200DolphinSchedulerUpgrader implements DolphinSchedulerUpgrader { private void convertConditions(List taskDefinitionLogList, Map taskNameCodeMap) throws Exception { for (TaskDefinitionLog taskDefinitionLog : taskDefinitionLogList) { - if (TASK_TYPE_CONDITIONS.equals(taskDefinitionLog.getTaskType())) { + if ("CONDITIONS".equals(taskDefinitionLog.getTaskType())) { ObjectMapper objectMapper = new ObjectMapper(); ObjectNode taskParams = JSONUtils.parseObject(taskDefinitionLog.getTaskParams()); // reset conditionResult diff --git a/dolphinscheduler-tools/src/main/java/org/apache/dolphinscheduler/tools/resource/MigrateResourceService.java b/dolphinscheduler-tools/src/main/java/org/apache/dolphinscheduler/tools/resource/MigrateResourceService.java index 8961af1223..26e06ec6fe 100644 --- a/dolphinscheduler-tools/src/main/java/org/apache/dolphinscheduler/tools/resource/MigrateResourceService.java +++ b/dolphinscheduler-tools/src/main/java/org/apache/dolphinscheduler/tools/resource/MigrateResourceService.java @@ -22,12 +22,11 @@ import static org.apache.dolphinscheduler.common.constants.Constants.FORMAT_S_S; import org.apache.dolphinscheduler.dao.entity.UdfFunc; import org.apache.dolphinscheduler.dao.mapper.TenantMapper; import org.apache.dolphinscheduler.dao.mapper.UdfFuncMapper; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.spi.enums.ResourceType; import org.apache.commons.lang3.StringUtils; -import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -50,7 +49,7 @@ public class MigrateResourceService { private static final Logger logger = LoggerFactory.getLogger(MigrateResourceService.class); @Autowired - private StorageOperate storageOperate; + private StorageOperator storageOperator; @Autowired private TenantMapper tenantMapper; @@ -83,11 +82,11 @@ public class MigrateResourceService { try { oriFullName = oriFullName.startsWith("/") ? oriFullName.substring(1) : oriFullName; if (ResourceType.FILE.getCode() == type) { - storageOperate.copy(oriFullName, + storageOperator.copy(oriFullName, String.format(FORMAT_S_S, resMigrateBasePath, oriFullName), true, true); } else if (ResourceType.UDF.getCode() == type) { String fullName = String.format(FORMAT_S_S, udfMigrateBasePath, oriFullName); - storageOperate.copy(oriFullName, fullName, true, true); + storageOperator.copy(oriFullName, fullName, true, true); // change relative udfs resourceName List udfs = udfFuncMapper.listUdfByResourceId(new Integer[]{id}); @@ -96,7 +95,7 @@ public class MigrateResourceService { udfFuncMapper.updateById(udf); }); } - } catch (IOException e) { + } catch (Exception e) { logger.error("Migrate resource: {} failed: {}", item, e); } } @@ -121,12 +120,11 @@ public class MigrateResourceService { } public String createMigrateDirByType(String targetTenantCode, ResourceType type) { - String migrateBasePath = type.equals(ResourceType.FILE) ? storageOperate.getResDir(targetTenantCode) - : storageOperate.getUdfDir(targetTenantCode); + String migrateBasePath = storageOperator.getStorageBaseDirectory(targetTenantCode, type); migrateBasePath += MIGRATE_BASE_DIR; try { - storageOperate.mkdir(targetTenantCode, migrateBasePath); - } catch (IOException e) { + storageOperator.createStorageDir(migrateBasePath); + } catch (Exception e) { logger.error("create migrate base directory {} failed", migrateBasePath); return ""; } diff --git a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/jupiter/DolphinSchedulerDatabaseContainerExtension.java b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/jupiter/DolphinSchedulerDatabaseContainerExtension.java index 4b463e1ef9..a264ca09e9 100644 --- a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/jupiter/DolphinSchedulerDatabaseContainerExtension.java +++ b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/jupiter/DolphinSchedulerDatabaseContainerExtension.java @@ -20,7 +20,6 @@ package org.apache.dolphinscheduler.tools.datasource.jupiter; import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; -import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; @@ -28,7 +27,6 @@ import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.testcontainers.containers.GenericContainer; -import org.testcontainers.lifecycle.Startables; import org.testcontainers.utility.DockerImageName; @Slf4j @@ -39,12 +37,6 @@ public class DolphinSchedulerDatabaseContainerExtension implements BeforeAllCall @Override public void beforeAll(ExtensionContext context) { databaseContainer = getDataSourceContainer(context); - log.info("Create {} successfully.", databaseContainer.getDockerImageName()); - databaseContainer.start(); - - log.info("Starting {}...", databaseContainer.getDockerImageName()); - Startables.deepStart(Stream.of(databaseContainer)).join(); - log.info("{} started", databaseContainer.getDockerImageName()); } diff --git a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/mysql/DolphinSchedulerMysqlProfile.java b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/mysql/DolphinSchedulerMysqlProfile.java index f32f4d80d4..a8670d5934 100644 --- a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/mysql/DolphinSchedulerMysqlProfile.java +++ b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/mysql/DolphinSchedulerMysqlProfile.java @@ -27,6 +27,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; @Inherited @@ -34,6 +35,7 @@ import org.springframework.test.context.ActiveProfiles; @Retention(RetentionPolicy.RUNTIME) @ActiveProfiles("mysql") @SpringBootTest(classes = {UpgradeDolphinScheduler.class, DaoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) public @interface DolphinSchedulerMysqlProfile { } diff --git a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/mysql/MysqlDatabaseContainerProvider.java b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/mysql/MysqlDatabaseContainerProvider.java index 80d0b8978d..d27a897920 100644 --- a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/mysql/MysqlDatabaseContainerProvider.java +++ b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/mysql/MysqlDatabaseContainerProvider.java @@ -20,15 +20,20 @@ package org.apache.dolphinscheduler.tools.datasource.mysql; import org.apache.dolphinscheduler.tools.datasource.jupiter.DatabaseContainerProvider; import org.apache.dolphinscheduler.tools.datasource.jupiter.DolphinSchedulerDatabaseContainer; +import java.util.stream.Stream; + +import lombok.extern.slf4j.Slf4j; + import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.lifecycle.Startables; import org.testcontainers.utility.DockerImageName; import com.google.auto.service.AutoService; -import com.google.common.collect.Lists; +@Slf4j @AutoService(DatabaseContainerProvider.class) public class MysqlDatabaseContainerProvider implements DatabaseContainerProvider { @@ -43,7 +48,19 @@ public class MysqlDatabaseContainerProvider implements DatabaseContainerProvider .withNetwork(NETWORK) .withExposedPorts(3306) .waitingFor(Wait.forHealthcheck()); - mysqlContainer.setPortBindings(Lists.newArrayList("3306:3306")); + + log.info("Create {} successfully.", mysqlContainer.getDockerImageName()); + mysqlContainer.start(); + + log.info("Starting {}...", mysqlContainer.getDockerImageName()); + Startables.deepStart(Stream.of(mysqlContainer)).join(); + log.info("{} started", mysqlContainer.getDockerImageName()); + + String jdbcUrl = "jdbc:mysql://localhost:" + mysqlContainer.getMappedPort(3306) + + "/dolphinscheduler?useUnicode=true&characterEncoding=UTF-8"; + System.clearProperty("spring.datasource.url"); + System.setProperty("spring.datasource.url", jdbcUrl); + return mysqlContainer; } diff --git a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/postgresql/DolphinSchedulerPostgresqlProfile.java b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/postgresql/DolphinSchedulerPostgresqlProfile.java index 1a5a07ad93..73b02f8a76 100644 --- a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/postgresql/DolphinSchedulerPostgresqlProfile.java +++ b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/postgresql/DolphinSchedulerPostgresqlProfile.java @@ -27,12 +27,14 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @ActiveProfiles("postgresql") @SpringBootTest(classes = {UpgradeDolphinScheduler.class, DaoConfiguration.class}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) public @interface DolphinSchedulerPostgresqlProfile { } diff --git a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/postgresql/PostgresqlDatabaseContainerProvider.java b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/postgresql/PostgresqlDatabaseContainerProvider.java index c09b7fae01..d8157d7029 100644 --- a/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/postgresql/PostgresqlDatabaseContainerProvider.java +++ b/dolphinscheduler-tools/src/test/java/org/apache/dolphinscheduler/tools/datasource/postgresql/PostgresqlDatabaseContainerProvider.java @@ -20,15 +20,17 @@ package org.apache.dolphinscheduler.tools.datasource.postgresql; import org.apache.dolphinscheduler.tools.datasource.jupiter.DatabaseContainerProvider; import org.apache.dolphinscheduler.tools.datasource.jupiter.DolphinSchedulerDatabaseContainer; +import java.util.stream.Stream; + import lombok.extern.slf4j.Slf4j; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.lifecycle.Startables; import org.testcontainers.utility.DockerImageName; import com.google.auto.service.AutoService; -import com.google.common.collect.Lists; @Slf4j @AutoService(DatabaseContainerProvider.class) @@ -47,8 +49,18 @@ public class PostgresqlDatabaseContainerProvider implements DatabaseContainerPro .withDatabaseName("dolphinscheduler") .withNetwork(NETWORK) .withExposedPorts(5432); - postgresqlContainer.setPortBindings(Lists.newArrayList("5432:5432")); + log.info("Create {} successfully.", postgresqlContainer.getDockerImageName()); + postgresqlContainer.start(); + + log.info("Starting {}...", postgresqlContainer.getDockerImageName()); + Startables.deepStart(Stream.of(postgresqlContainer)).join(); + log.info("{} started", postgresqlContainer.getDockerImageName()); + + String jdbcUrl = "jdbc:mysql://localhost:" + postgresqlContainer.getMappedPort(5432) + + "/dolphinscheduler?useUnicode=true&characterEncoding=UTF-8"; + System.clearProperty("spring.datasource.url"); + System.setProperty("spring.datasource.url", jdbcUrl); return postgresqlContainer; } diff --git a/dolphinscheduler-ui/src/views/resource/components/resource/edit/use-edit.ts b/dolphinscheduler-ui/src/views/resource/components/resource/edit/use-edit.ts index ab6c05e463..2ec97926b3 100644 --- a/dolphinscheduler-ui/src/views/resource/components/resource/edit/use-edit.ts +++ b/dolphinscheduler-ui/src/views/resource/components/resource/edit/use-edit.ts @@ -32,7 +32,7 @@ export function useEdit(state: any) { const getResourceView = (fullName: string, tenantCode: string) => { const params = { skipLineNum: 0, - limit: 3000, + limit: -1, fullName: fullName, tenantCode: tenantCode } diff --git a/dolphinscheduler-ui/src/views/resource/components/resource/types.ts b/dolphinscheduler-ui/src/views/resource/components/resource/types.ts index e33af74686..f208380178 100644 --- a/dolphinscheduler-ui/src/views/resource/components/resource/types.ts +++ b/dolphinscheduler-ui/src/views/resource/components/resource/types.ts @@ -133,7 +133,6 @@ export interface IUploadDefaultValue { name: string file: string type: ResourceType - pid: number currentDir: string } } diff --git a/dolphinscheduler-ui/src/views/resource/components/resource/use-file.ts b/dolphinscheduler-ui/src/views/resource/components/resource/use-file.ts index 5c9207c21a..bcac6e76a9 100644 --- a/dolphinscheduler-ui/src/views/resource/components/resource/use-file.ts +++ b/dolphinscheduler-ui/src/views/resource/components/resource/use-file.ts @@ -72,7 +72,7 @@ export function useFileState( const getResourceView = (fullName: string, tenantCode: string) => { const params = { skipLineNum: 0, - limit: 3000, + limit: -1, fullName: fullName, tenantCode: tenantCode } diff --git a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/WorkerServer.java b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/WorkerServer.java index 797d8f78a3..a7b49fbd81 100644 --- a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/WorkerServer.java +++ b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/WorkerServer.java @@ -86,7 +86,7 @@ public class WorkerServer implements IStoppable { @PostConstruct public void run() { this.workerRpcServer.start(); - TaskPluginManager.loadPlugin(); + TaskPluginManager.loadTaskPlugin(); DataSourceProcessorProvider.initialize(); this.workerRegistryClient.setRegistryStoppable(this); diff --git a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/registry/WorkerRegistryClient.java b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/registry/WorkerRegistryClient.java index eb7b8d32d2..e963fc0c13 100644 --- a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/registry/WorkerRegistryClient.java +++ b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/registry/WorkerRegistryClient.java @@ -97,11 +97,12 @@ public class WorkerRegistryClient implements AutoCloseable { workerHeartBeat = workerHeartBeatTask.getHeartBeat(); Thread.sleep(SLEEP_TIME_MILLIS); } - String workerZKPath = workerConfig.getWorkerRegistryPath(); + String workerRegistryPath = workerConfig.getWorkerRegistryPath(); // remove before persist - registryClient.remove(workerZKPath); - registryClient.persistEphemeral(workerZKPath, JSONUtils.toJsonString(workerHeartBeat)); - log.info("Worker node: {} registry to ZK {} successfully", workerConfig.getWorkerAddress(), workerZKPath); + registryClient.remove(workerRegistryPath); + registryClient.persistEphemeral(workerRegistryPath, JSONUtils.toJsonString(workerHeartBeat)); + log.info("Worker node: {} registry to registry center {} successfully", workerConfig.getWorkerAddress(), + workerRegistryPath); while (!registryClient.checkNodeExists(workerConfig.getWorkerAddress(), RegistryNodeType.WORKER)) { ThreadUtils.sleep(SLEEP_TIME_MILLIS); diff --git a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutor.java b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutor.java index ea44f1790f..f5abf9e577 100644 --- a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutor.java +++ b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutor.java @@ -17,7 +17,7 @@ package org.apache.dolphinscheduler.server.worker.runner; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskCallBack; import org.apache.dolphinscheduler.plugin.task.api.TaskException; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; @@ -34,12 +34,12 @@ public class DefaultWorkerTaskExecutor extends WorkerTaskExecutor { public DefaultWorkerTaskExecutor(@NonNull TaskExecutionContext taskExecutionContext, @NonNull WorkerConfig workerConfig, @NonNull WorkerMessageSender workerMessageSender, - @Nullable StorageOperate storageOperate, + @Nullable StorageOperator storageOperator, @NonNull WorkerRegistryClient workerRegistryClient) { super(taskExecutionContext, workerConfig, workerMessageSender, - storageOperate, + storageOperator, workerRegistryClient); } diff --git a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutorFactory.java b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutorFactory.java index 20fa6a5e2a..085deafb09 100644 --- a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutorFactory.java +++ b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutorFactory.java @@ -17,7 +17,7 @@ package org.apache.dolphinscheduler.server.worker.runner; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.server.worker.config.WorkerConfig; import org.apache.dolphinscheduler.server.worker.registry.WorkerRegistryClient; @@ -34,18 +34,18 @@ public class DefaultWorkerTaskExecutorFactory private final @NonNull TaskExecutionContext taskExecutionContext; private final @NonNull WorkerConfig workerConfig; private final @NonNull WorkerMessageSender workerMessageSender; - private final @Nullable StorageOperate storageOperate; + private final @Nullable StorageOperator storageOperator; private final @NonNull WorkerRegistryClient workerRegistryClient; public DefaultWorkerTaskExecutorFactory(@NonNull TaskExecutionContext taskExecutionContext, @NonNull WorkerConfig workerConfig, @NonNull WorkerMessageSender workerMessageSender, - @Nullable StorageOperate storageOperate, + @Nullable StorageOperator storageOperator, @NonNull WorkerRegistryClient workerRegistryClient) { this.taskExecutionContext = taskExecutionContext; this.workerConfig = workerConfig; this.workerMessageSender = workerMessageSender; - this.storageOperate = storageOperate; + this.storageOperator = storageOperator; this.workerRegistryClient = workerRegistryClient; } @@ -55,7 +55,7 @@ public class DefaultWorkerTaskExecutorFactory taskExecutionContext, workerConfig, workerMessageSender, - storageOperate, + storageOperator, workerRegistryClient); } } diff --git a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/WorkerTaskExecutor.java b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/WorkerTaskExecutor.java index 604b0b5743..8635ed4600 100644 --- a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/WorkerTaskExecutor.java +++ b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/WorkerTaskExecutor.java @@ -33,7 +33,7 @@ import org.apache.dolphinscheduler.extract.base.client.SingletonJdkDynamicRpcCli import org.apache.dolphinscheduler.extract.base.utils.Host; import org.apache.dolphinscheduler.extract.master.transportor.ITaskInstanceExecutionEvent; import org.apache.dolphinscheduler.plugin.datasource.api.utils.CommonUtils; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskCallBack; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; @@ -76,7 +76,7 @@ public abstract class WorkerTaskExecutor implements Runnable { protected final TaskExecutionContext taskExecutionContext; protected final WorkerConfig workerConfig; protected final WorkerMessageSender workerMessageSender; - protected final @Nullable StorageOperate storageOperate; + protected final @Nullable StorageOperator storageOperator; protected final WorkerRegistryClient workerRegistryClient; protected @Nullable AbstractTask task; @@ -85,12 +85,12 @@ public abstract class WorkerTaskExecutor implements Runnable { @NonNull TaskExecutionContext taskExecutionContext, @NonNull WorkerConfig workerConfig, @NonNull WorkerMessageSender workerMessageSender, - @Nullable StorageOperate storageOperate, + @Nullable StorageOperator storageOperator, @NonNull WorkerRegistryClient workerRegistryClient) { this.taskExecutionContext = taskExecutionContext; this.workerConfig = workerConfig; this.workerMessageSender = workerMessageSender; - this.storageOperate = storageOperate; + this.storageOperator = storageOperator; this.workerRegistryClient = workerRegistryClient; SensitiveDataConverter.addMaskPattern(K8S_CONFIG_REGEX); } @@ -217,18 +217,18 @@ public abstract class WorkerTaskExecutor implements Runnable { log.info("WorkflowInstanceExecDir: {} check successfully", taskExecutionContext.getExecutePath()); TaskChannel taskChannel = - Optional.ofNullable(TaskPluginManager.getTaskChannelMap().get(taskExecutionContext.getTaskType())) + Optional.ofNullable(TaskPluginManager.getTaskChannel(taskExecutionContext.getTaskType())) .orElseThrow(() -> new TaskPluginException(taskExecutionContext.getTaskType() + " task plugin not found, please check the task type is correct.")); log.info("Create TaskChannel: {} successfully", taskChannel.getClass().getName()); - ResourceContext resourceContext = TaskExecutionContextUtils.downloadResourcesIfNeeded(originTenant, taskChannel, - storageOperate, taskExecutionContext); + ResourceContext resourceContext = TaskExecutionContextUtils.downloadResourcesIfNeeded(taskChannel, + storageOperator, taskExecutionContext); taskExecutionContext.setResourceContext(resourceContext); log.info("Download resources successfully: \n{}", taskExecutionContext.getResourceContext()); - TaskFilesTransferUtils.downloadUpstreamFiles(taskExecutionContext, storageOperate); + TaskFilesTransferUtils.downloadUpstreamFiles(taskExecutionContext, storageOperator); log.info("Download upstream files: {} successfully", TaskFilesTransferUtils.getFileLocalParams(taskExecutionContext, Direct.IN)); @@ -282,7 +282,7 @@ public abstract class WorkerTaskExecutor implements Runnable { taskExecutionContext.setEndTime(System.currentTimeMillis()); // upload out files and modify the "OUT FILE" property in VarPool - TaskFilesTransferUtils.uploadOutputFiles(taskExecutionContext, storageOperate); + TaskFilesTransferUtils.uploadOutputFiles(taskExecutionContext, storageOperator); log.info("Upload output files: {} successfully", TaskFilesTransferUtils.getFileLocalParams(taskExecutionContext, Direct.OUT)); diff --git a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/WorkerTaskExecutorFactoryBuilder.java b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/WorkerTaskExecutorFactoryBuilder.java index 56f3207884..4cb1739abc 100644 --- a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/WorkerTaskExecutorFactoryBuilder.java +++ b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/runner/WorkerTaskExecutorFactoryBuilder.java @@ -17,7 +17,7 @@ package org.apache.dolphinscheduler.server.worker.runner; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.server.worker.config.WorkerConfig; import org.apache.dolphinscheduler.server.worker.registry.WorkerRegistryClient; @@ -36,7 +36,7 @@ public class WorkerTaskExecutorFactoryBuilder { private WorkerMessageSender workerMessageSender; @Autowired(required = false) - private StorageOperate storageOperate; + private StorageOperator storageOperator; @Autowired private WorkerRegistryClient workerRegistryClient; @@ -45,11 +45,11 @@ public class WorkerTaskExecutorFactoryBuilder { WorkerConfig workerConfig, WorkerMessageSender workerMessageSender, WorkerTaskExecutorThreadPool workerManager, - StorageOperate storageOperate, + StorageOperator storageOperator, WorkerRegistryClient workerRegistryClient) { this.workerConfig = workerConfig; this.workerMessageSender = workerMessageSender; - this.storageOperate = storageOperate; + this.storageOperator = storageOperator; this.workerRegistryClient = workerRegistryClient; } @@ -57,7 +57,7 @@ public class WorkerTaskExecutorFactoryBuilder { return new DefaultWorkerTaskExecutorFactory(taskExecutionContext, workerConfig, workerMessageSender, - storageOperate, + storageOperator, workerRegistryClient); } diff --git a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/utils/TaskExecutionContextUtils.java b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/utils/TaskExecutionContextUtils.java index 1b486f265c..e23e4152cb 100644 --- a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/utils/TaskExecutionContextUtils.java +++ b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/utils/TaskExecutionContextUtils.java @@ -18,13 +18,13 @@ package org.apache.dolphinscheduler.server.worker.utils; import org.apache.dolphinscheduler.common.utils.FileUtils; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.ResourceMetadata; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskChannel; import org.apache.dolphinscheduler.plugin.task.api.TaskException; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.model.ResourceInfo; import org.apache.dolphinscheduler.plugin.task.api.parameters.AbstractParameters; -import org.apache.dolphinscheduler.plugin.task.api.parameters.ParametersNode; import org.apache.dolphinscheduler.plugin.task.api.resource.ResourceContext; import org.apache.dolphinscheduler.server.worker.metrics.WorkerServerMetrics; @@ -65,15 +65,10 @@ public class TaskExecutionContextUtils { } } - public static ResourceContext downloadResourcesIfNeeded(String tenant, - TaskChannel taskChannel, - StorageOperate storageOperate, + public static ResourceContext downloadResourcesIfNeeded(TaskChannel taskChannel, + StorageOperator storageOperator, TaskExecutionContext taskExecutionContext) { - AbstractParameters abstractParameters = taskChannel.parseParameters( - ParametersNode.builder() - .taskType(taskExecutionContext.getTaskType()) - .taskParams(taskExecutionContext.getTaskParams()) - .build()); + AbstractParameters abstractParameters = taskChannel.parseParameters(taskExecutionContext.getTaskParams()); List resourceFilesList = abstractParameters.getResourceFilesList(); if (CollectionUtils.isEmpty(resourceFilesList)) { @@ -86,14 +81,15 @@ public class TaskExecutionContextUtils { for (ResourceInfo resourceInfo : resourceFilesList) { String resourceAbsolutePathInStorage = resourceInfo.getResourceName(); - String resourceRelativePath = storageOperate.getResourceFileName(tenant, resourceAbsolutePathInStorage); - String resourceAbsolutePathInLocal = Paths.get(taskWorkingDirectory, resourceRelativePath).toString(); + ResourceMetadata resourceMetaData = storageOperator.getResourceMetaData(resourceAbsolutePathInStorage); + String resourceAbsolutePathInLocal = + Paths.get(taskWorkingDirectory, resourceMetaData.getResourceRelativePath()).toString(); File file = new File(resourceAbsolutePathInLocal); if (!file.exists()) { try { long resourceDownloadStartTime = System.currentTimeMillis(); - storageOperate.download(resourceAbsolutePathInStorage, resourceAbsolutePathInLocal, true); - log.debug("Download resource file {} under: {} successfully", resourceAbsolutePathInStorage, + storageOperator.download(resourceAbsolutePathInStorage, resourceAbsolutePathInLocal, true); + log.info("Download resource file {} -> {} successfully", resourceAbsolutePathInStorage, resourceAbsolutePathInLocal); FileUtils.setFileTo755(file); WorkerServerMetrics diff --git a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/utils/TaskFilesTransferUtils.java b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/utils/TaskFilesTransferUtils.java index f060c0a17d..47cd9bdaf8 100644 --- a/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/utils/TaskFilesTransferUtils.java +++ b/dolphinscheduler-worker/src/main/java/org/apache/dolphinscheduler/server/worker/utils/TaskFilesTransferUtils.java @@ -22,7 +22,7 @@ import static org.apache.dolphinscheduler.common.constants.Constants.CRC_SUFFIX; import org.apache.dolphinscheduler.common.utils.DateUtils; import org.apache.dolphinscheduler.common.utils.FileUtils; import org.apache.dolphinscheduler.common.utils.JSONUtils; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskException; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.enums.DataType; @@ -65,11 +65,11 @@ public class TaskFilesTransferUtils { * upload output files to resource storage * * @param taskExecutionContext is the context of task - * @param storageOperate is the storage operate + * @param storageOperator is the storage operate * @throws TaskException TaskException */ public static void uploadOutputFiles(TaskExecutionContext taskExecutionContext, - StorageOperate storageOperate) throws TaskException { + StorageOperator storageOperator) throws TaskException { // get OUTPUT FILE parameters List localParamsProperty = getFileLocalParams(taskExecutionContext, Direct.OUT); if (localParamsProperty.isEmpty()) { @@ -102,15 +102,15 @@ public class TaskFilesTransferUtils { try { // upload file to storage String resourceWholePath = - storageOperate.getResourceFullName(taskExecutionContext.getTenantCode(), resourcePath); + storageOperator.getStorageFileAbsolutePath(taskExecutionContext.getTenantCode(), resourcePath); String resourceCRCWholePath = - storageOperate.getResourceFullName(taskExecutionContext.getTenantCode(), resourceCRCPath); + storageOperator.getStorageFileAbsolutePath(taskExecutionContext.getTenantCode(), + resourceCRCPath); log.info("{} --- Local:{} to Remote:{}", property, srcPath, resourceWholePath); - storageOperate.upload(taskExecutionContext.getTenantCode(), srcPath, resourceWholePath, false, true); + storageOperator.upload(srcPath, resourceWholePath, false, true); log.info("{} --- Local:{} to Remote:{}", "CRC file", srcCRCPath, resourceCRCWholePath); - storageOperate.upload(taskExecutionContext.getTenantCode(), srcCRCPath, resourceCRCWholePath, false, - true); - } catch (IOException ex) { + storageOperator.upload(srcCRCPath, resourceCRCWholePath, false, true); + } catch (Exception ex) { throw new TaskException("Upload file to storage error", ex); } @@ -134,10 +134,11 @@ public class TaskFilesTransferUtils { * only download files which are defined in the task parameters * * @param taskExecutionContext is the context of task - * @param storageOperate is the storage operate + * @param storageOperator is the storage operate * @throws TaskException task exception */ - public static void downloadUpstreamFiles(TaskExecutionContext taskExecutionContext, StorageOperate storageOperate) { + public static void downloadUpstreamFiles(TaskExecutionContext taskExecutionContext, + StorageOperator storageOperator) { // get "IN FILE" parameters List localParamsProperty = getFileLocalParams(taskExecutionContext, Direct.IN); @@ -178,14 +179,10 @@ public class TaskFilesTransferUtils { downloadPath = targetPath; } - try { - String resourceWholePath = - storageOperate.getResourceFullName(taskExecutionContext.getTenantCode(), resourcePath); - log.info("{} --- Remote:{} to Local:{}", property, resourceWholePath, downloadPath); - storageOperate.download(resourceWholePath, downloadPath, true); - } catch (IOException ex) { - throw new TaskException("Download file from storage error", ex); - } + String resourceWholePath = + storageOperator.getStorageFileAbsolutePath(taskExecutionContext.getTenantCode(), resourcePath); + log.info("{} --- Remote:{} to Local:{}", property, resourceWholePath, downloadPath); + storageOperator.download(resourceWholePath, downloadPath, true); // unpack if the data is packaged if (isPack) { diff --git a/dolphinscheduler-worker/src/main/resources/application.yaml b/dolphinscheduler-worker/src/main/resources/application.yaml index a4a7476984..e6434e4f9d 100644 --- a/dolphinscheduler-worker/src/main/resources/application.yaml +++ b/dolphinscheduler-worker/src/main/resources/application.yaml @@ -23,6 +23,7 @@ spring: autoconfigure: exclude: - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration + cloud.discovery.client.composite-indicator.enabled: false registry: type: zookeeper @@ -30,12 +31,12 @@ registry: namespace: dolphinscheduler connect-string: localhost:2181 retry-policy: - base-sleep-time: 60ms - max-sleep: 300ms + base-sleep-time: 1s + max-sleep: 3s max-retries: 5 - session-timeout: 30s - connection-timeout: 9s - block-until-connected: 600ms + session-timeout: 60s + connection-timeout: 15s + block-until-connected: 15s digest: ~ worker: diff --git a/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutorTest.java b/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutorTest.java index e211fcdd1f..f451c09012 100644 --- a/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutorTest.java +++ b/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/runner/DefaultWorkerTaskExecutorTest.java @@ -18,7 +18,7 @@ package org.apache.dolphinscheduler.server.worker.runner; import org.apache.dolphinscheduler.common.constants.Constants; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.enums.TaskExecutionStatus; import org.apache.dolphinscheduler.server.worker.config.WorkerConfig; @@ -39,7 +39,7 @@ public class DefaultWorkerTaskExecutorTest { private WorkerMessageSender workerMessageSender = Mockito.mock(WorkerMessageSender.class); - private StorageOperate storageOperate = Mockito.mock(StorageOperate.class); + private StorageOperator storageOperator = Mockito.mock(StorageOperator.class); private WorkerRegistryClient workerRegistryClient = Mockito.mock(WorkerRegistryClient.class); @@ -55,7 +55,7 @@ public class DefaultWorkerTaskExecutorTest { taskExecutionContext, workerConfig, workerMessageSender, - storageOperate, + storageOperator, workerRegistryClient); Assertions.assertAll(workerTaskExecutor::run); @@ -78,7 +78,7 @@ public class DefaultWorkerTaskExecutorTest { taskExecutionContext, workerConfig, workerMessageSender, - storageOperate, + storageOperator, workerRegistryClient); Assertions.assertAll(workerTaskExecutor::run); diff --git a/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/runner/operator/TaskInstanceOperationFunctionTest.java b/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/runner/operator/TaskInstanceOperationFunctionTest.java index f761dd61d6..cc17d74e02 100644 --- a/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/runner/operator/TaskInstanceOperationFunctionTest.java +++ b/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/runner/operator/TaskInstanceOperationFunctionTest.java @@ -31,7 +31,7 @@ import org.apache.dolphinscheduler.extract.worker.transportor.TaskInstancePauseR import org.apache.dolphinscheduler.extract.worker.transportor.TaskInstancePauseResponse; import org.apache.dolphinscheduler.extract.worker.transportor.UpdateWorkflowHostRequest; import org.apache.dolphinscheduler.extract.worker.transportor.UpdateWorkflowHostResponse; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.AbstractTask; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.utils.LogUtils; @@ -71,7 +71,7 @@ public class TaskInstanceOperationFunctionTest { private WorkerTaskExecutorThreadPool workerManager = Mockito.mock(WorkerTaskExecutorThreadPool.class); - private StorageOperate storageOperate = Mockito.mock(StorageOperate.class); + private StorageOperator storageOperator = Mockito.mock(StorageOperator.class); private WorkerRegistryClient workerRegistryClient = Mockito.mock(WorkerRegistryClient.class); @@ -92,7 +92,7 @@ public class TaskInstanceOperationFunctionTest { workerConfig, workerMessageSender, workerManager, - storageOperate, + storageOperator, workerRegistryClient); TaskInstanceDispatchOperationFunction taskInstanceDispatchOperationFunction = @@ -186,7 +186,7 @@ public class TaskInstanceOperationFunctionTest { workerConfig, workerMessageSender, workerManager, - storageOperate, + storageOperator, workerRegistryClient); TaskInstanceDispatchOperationFunction taskInstanceDispatchOperationFunction = diff --git a/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/utils/TaskFilesTransferUtilsTest.java b/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/utils/TaskFilesTransferUtilsTest.java index 9b8d5a9129..a5a425f9a9 100644 --- a/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/utils/TaskFilesTransferUtilsTest.java +++ b/dolphinscheduler-worker/src/test/java/org/apache/dolphinscheduler/server/worker/utils/TaskFilesTransferUtilsTest.java @@ -18,7 +18,7 @@ package org.apache.dolphinscheduler.server.worker.utils; import org.apache.dolphinscheduler.common.utils.DateUtils; -import org.apache.dolphinscheduler.plugin.storage.api.StorageOperate; +import org.apache.dolphinscheduler.plugin.storage.api.StorageOperator; import org.apache.dolphinscheduler.plugin.task.api.TaskExecutionContext; import org.apache.dolphinscheduler.plugin.task.api.enums.DataType; import org.apache.dolphinscheduler.plugin.task.api.enums.Direct; @@ -99,8 +99,8 @@ public class TaskFilesTransferUtilsTest { List oriProperties = TaskFilesTransferUtils.getVarPools(taskExecutionContext); - StorageOperate storageOperate = Mockito.mock(StorageOperate.class); - TaskFilesTransferUtils.uploadOutputFiles(taskExecutionContext, storageOperate); + StorageOperator storageOperator = Mockito.mock(StorageOperator.class); + TaskFilesTransferUtils.uploadOutputFiles(taskExecutionContext, storageOperator); System.out.println(taskExecutionContext.getVarPool()); String exceptFolder = @@ -154,10 +154,10 @@ public class TaskFilesTransferUtilsTest { .endTime(endTime) .build(); - StorageOperate storageOperate = Mockito.mock(StorageOperate.class); + StorageOperator storageOperator = Mockito.mock(StorageOperator.class); Mockito.mockStatic(ZipUtil.class); Assertions.assertDoesNotThrow( - () -> TaskFilesTransferUtils.downloadUpstreamFiles(taskExecutionContext, storageOperate)); + () -> TaskFilesTransferUtils.downloadUpstreamFiles(taskExecutionContext, storageOperator)); } @Test diff --git a/pom.xml b/pom.xml index aeadd3adc0..ee2327fce8 100755 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ false 3.2.0 3.0.0 - 7.1.2 + 9.2.0 1.18.20 4.2.0 1.4.2 diff --git a/script/env/install_env.sh b/script/env/install_env.sh deleted file mode 100644 index 8de1c78637..0000000000 --- a/script/env/install_env.sh +++ /dev/null @@ -1,63 +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. -# - -# --------------------------------------------------------- -# INSTALL MACHINE -# --------------------------------------------------------- -# A comma separated list of machine hostname or IP would be installed DolphinScheduler, -# including master, worker, api, alert. If you want to deploy in pseudo-distributed -# mode, just write a pseudo-distributed hostname -# Example for hostnames: ips="ds1,ds2,ds3,ds4,ds5", Example for IPs: ips="192.168.8.1,192.168.8.2,192.168.8.3,192.168.8.4,192.168.8.5" -ips=${ips:-"ds1,ds2,ds3,ds4,ds5"} - -# Port of SSH protocol, default value is 22. For now we only support same port in all `ips` machine -# modify it if you use different ssh port -sshPort=${sshPort:-"22"} - -# A comma separated list of machine hostname or IP would be installed Master server, it -# must be a subset of configuration `ips`. -# Example for hostnames: masters="ds1,ds2", Example for IPs: masters="192.168.8.1,192.168.8.2" -masters=${masters:-"ds1,ds2"} - -# A comma separated list of machine : or :.All hostname or IP must be a -# subset of configuration `ips`, And workerGroup have default value as `default`, but we recommend you declare behind the hosts -# Example for hostnames: workers="ds1:default,ds2:default,ds3:default", Example for IPs: workers="192.168.8.1:default,192.168.8.2:default,192.168.8.3:default" -workers=${workers:-"ds1:default,ds2:default,ds3:default,ds4:default,ds5:default"} - -# A comma separated list of machine hostname or IP would be installed Alert server, it -# must be a subset of configuration `ips`. -# Example for hostname: alertServer="ds3", Example for IP: alertServer="192.168.8.3" -alertServer=${alertServer:-"ds3"} - -# A comma separated list of machine hostname or IP would be installed API server, it -# must be a subset of configuration `ips`. -# Example for hostname: apiServers="ds1", Example for IP: apiServers="192.168.8.1" -apiServers=${apiServers:-"ds1"} - -# The directory to install DolphinScheduler for all machine we config above. It will automatically be created by `install.sh` script if not exists. -# Do not set this configuration same as the current path (pwd). Do not add quotes to it if you using related path. -installPath=${installPath:-"/tmp/dolphinscheduler"} - -# The user to deploy DolphinScheduler for all machine we config above. For now user must create by yourself before running `install.sh` -# script. The user needs to have sudo privileges and permissions to operate hdfs. If hdfs is enabled than the root directory needs -# to be created by this user -deployUser=${deployUser:-"dolphinscheduler"} - -# The root of zookeeper, for now DolphinScheduler default registry server is zookeeper. -# It will delete ${zkRoot} in the zookeeper when you run install.sh, so please keep it same as registry.zookeeper.namespace in yml files. -# Similarly, if you want to modify the value, please modify registry.zookeeper.namespace in yml files as well. -zkRoot=${zkRoot:-"/dolphinscheduler"} diff --git a/script/install.sh b/script/install.sh deleted file mode 100755 index d36f90a3f3..0000000000 --- a/script/install.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -# -# 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. -# - -workDir=`dirname $0` -workDir=`cd ${workDir};pwd` -baseDir=`cd ${workDir}/..;pwd` - -source ${workDir}/env/install_env.sh -source ${workDir}/env/dolphinscheduler_env.sh - -echo "1.create directory" - -# If install Path equal to "/" or related path is "/" or is empty, will cause directory "/bin" be overwrite or file adding, -# so we should check its value. Here use command `realpath` to get the related path, and it will skip if your shell env -# without command `realpath`. -if [ ${baseDir} = $installPath ]; then - echo "Fatal: The installPath can not be same as the current path: ${installPath}" - exit 1 -elif [ ! -d $installPath ];then - sudo mkdir -p $installPath - sudo chown -R $deployUser:$deployUser $installPath -elif [[ -z "${installPath// }" || "${installPath// }" == "/" || ( $(command -v realpath) && $(realpath -s "${installPath}") == "/" ) ]]; then - echo "Parameter installPath can not be empty, use in root path or related path of root path, currently use ${installPath}" - exit 1 -fi - -echo "2.scp resources" -bash ${workDir}/scp-hosts.sh -if [ $? -eq 0 ];then - echo 'scp copy completed' -else - echo 'scp copy failed to exit' - exit 1 -fi - -echo "3.stop server" -bash ${workDir}/stop-all.sh - -echo "4.delete zk node" -bash ${workDir}/remove-zk-node.sh $zkRoot - -echo "5.startup" -bash ${workDir}/start-all.sh